Added recaptcha compatibility

This commit is contained in:
Liam Cardenas 2017-12-22 18:09:06 -08:00
parent 4c93119f73
commit 5948124586
6 changed files with 313 additions and 203 deletions

View file

@ -1,52 +1,62 @@
// Module imports // Module imports
const {app, BrowserWindow, ipcMain, Menu, Tray, globalShortcut} = require('electron'); const {
const path = require('path'); app,
const url = require('url'); BrowserWindow,
const jayson = require('jayson'); ipcMain,
const semver = require('semver'); Menu,
const https = require('https'); Tray,
const keytar = require('keytar'); globalShortcut,
} = require("electron");
const path = require("path");
const url = require("url");
const jayson = require("jayson");
const semver = require("semver");
const https = require("https");
const keytar = require("keytar");
// tree-kill has better cross-platform handling of // tree-kill has better cross-platform handling of
// killing a process. child-process.kill was unreliable // killing a process. child-process.kill was unreliable
const kill = require('tree-kill'); const kill = require("tree-kill");
const child_process = require('child_process'); const child_process = require("child_process");
const assert = require('assert'); const assert = require("assert");
const localVersion = app.getVersion(); const localVersion = app.getVersion();
const setMenu = require('./menu/main-menu.js'); const setMenu = require("./menu/main-menu.js");
export const contextMenu = require('./menu/context-menu'); export const contextMenu = require("./menu/context-menu");
// Debug configs // Debug configs
const isDevelopment = process.env.NODE_ENV === 'development'; const isDevelopment = process.env.NODE_ENV === "development";
if (isDevelopment) { if (isDevelopment) {
try try {
{ const {
const { default: installExtension, REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS } = require('electron-devtools-installer'); default: installExtension,
app.on('ready', () => { REACT_DEVELOPER_TOOLS,
REDUX_DEVTOOLS,
} = require("electron-devtools-installer");
app.on("ready", () => {
[REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS].forEach(extension => { [REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS].forEach(extension => {
installExtension(extension) installExtension(extension)
.then((name) => console.log(`Added Extension: ${name}`)) .then(name => console.log(`Added Extension: ${name}`))
.catch((err) => console.log('An error occurred: ', err)); .catch(err => console.log("An error occurred: ", err));
}); });
}); });
} } catch (err) {
catch (err) console.error(err);
{
console.error(err)
} }
} }
// Misc constants // Misc constants
const LATEST_RELEASE_API_URL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest'; const LATEST_RELEASE_API_URL =
const DAEMON_PATH = process.env.LBRY_DAEMON || path.join(__static, 'daemon/lbrynet-daemon'); "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 const rendererUrl = isDevelopment
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`; : `file://${__dirname}/index.html`;
let client = jayson.client.http({ let client = jayson.client.http({
host: 'localhost', host: "localhost",
port: 5279, port: 5279,
path: '/', path: "/",
timeout: 1000 timeout: 1000,
}); });
// Keep a global reference of the window object, if you don't, the window will // Keep a global reference of the window object, if you don't, the window will
@ -84,8 +94,8 @@ function processRequestedUri(uri) {
// lbry://channel/#claimid. We remove the slash here as well. // lbry://channel/#claimid. We remove the slash here as well.
// On Linux and Mac, we just return the URI as given. // On Linux and Mac, we just return the URI as given.
if (process.platform === 'win32') { if (process.platform === "win32") {
return uri.replace(/\/$/, '').replace('/#', '#'); return uri.replace(/\/$/, "").replace("/#", "#");
} else { } else {
return uri; return uri;
} }
@ -97,69 +107,87 @@ function processRequestedUri(uri) {
* when no windows are open. * when no windows are open.
*/ */
function openItem(fullPath) { function openItem(fullPath) {
const subprocOptions = { const subprocOptions = {
detached: true, detached: true,
stdio: 'ignore', stdio: "ignore",
}; };
let child; let child;
if (process.platform === 'darwin') { if (process.platform === "darwin") {
child = child_process.spawn('open', [fullPath], subprocOptions); child = child_process.spawn("open", [fullPath], subprocOptions);
} else if (process.platform === 'linux') { } else if (process.platform === "linux") {
child = child_process.spawn('xdg-open', [fullPath], subprocOptions); child = child_process.spawn("xdg-open", [fullPath], subprocOptions);
} else if (process.platform === 'win32') { } else if (process.platform === "win32") {
child = child_process.spawn(fullPath, Object.assign({}, subprocOptions, {shell: true})); child = child_process.spawn(
} fullPath,
Object.assign({}, subprocOptions, { shell: true })
);
}
// Causes child process reference to be garbage collected, allowing main process to exit // Causes child process reference to be garbage collected, allowing main process to exit
child.unref(); child.unref();
} }
function getPidsForProcessName(name) { function getPidsForProcessName(name) {
if (process.platform === 'win32') { if (process.platform === "win32") {
const tasklistOut = child_process.execSync(`tasklist /fi "Imagename eq ${name}.exe" /nh`, {encoding: 'utf8'}); const tasklistOut = child_process.execSync(
if (tasklistOut.startsWith('INFO')) { `tasklist /fi "Imagename eq ${name}.exe" /nh`,
{ encoding: "utf8" }
);
if (tasklistOut.startsWith("INFO")) {
return []; return [];
} else { } else {
return tasklistOut.match(/[^\r\n]+/g).map((line) => line.split(/\s+/)[1]); // Second column of every non-empty line return tasklistOut.match(/[^\r\n]+/g).map(line => line.split(/\s+/)[1]); // Second column of every non-empty line
} }
} else { } else {
const pgrepOut = child_process.spawnSync('pgrep', ['-x', name], {encoding: 'utf8'}).stdout; const pgrepOut = child_process.spawnSync("pgrep", ["-x", name], {
encoding: "utf8",
}).stdout;
return pgrepOut.match(/\d+/g); return pgrepOut.match(/\d+/g);
} }
} }
function createWindow () { function createWindow() {
// Disable renderer process's webSecurity on development to enable CORS. // Disable renderer process's webSecurity on development to enable CORS.
win = isDevelopment win = isDevelopment
? new BrowserWindow({backgroundColor: '#155B4A', minWidth: 800, minHeight: 600, webPreferences: {webSecurity: false}}) ? new BrowserWindow({
: new BrowserWindow({backgroundColor: '#155B4A', minWidth: 800, minHeight: 600}); backgroundColor: "#155B4A",
minWidth: 800,
minHeight: 600,
webPreferences: { webSecurity: false },
})
: new BrowserWindow({
backgroundColor: "#155B4A",
minWidth: 800,
minHeight: 600,
});
win.webContents.session.setUserAgent(`LBRY/${localVersion}`); win.webContents.session.setUserAgent(`LBRY/${localVersion}`);
win.maximize() win.maximize();
if (isDevelopment) { if (isDevelopment) {
win.webContents.openDevTools(); win.webContents.openDevTools();
} }
win.loadURL(rendererUrl) win.loadURL(rendererUrl);
if (openUri) { // We stored and received a URI that an external app requested before we had a window object if (openUri) {
win.webContents.on('did-finish-load', () => { // We stored and received a URI that an external app requested before we had a window object
win.webContents.send('open-uri-requested', openUri); win.webContents.on("did-finish-load", () => {
win.webContents.send("open-uri-requested", openUri, true);
}); });
} }
win.removeAllListeners(); win.removeAllListeners();
win.on('close', function(event) { win.on("close", function(event) {
if (minimize) { if (minimize) {
event.preventDefault(); event.preventDefault();
win.hide(); win.hide();
} }
}) });
win.on('closed', () => { win.on("closed", () => {
win = null win = null;
}) });
win.on("hide", () => { win.on("hide", () => {
// Checks what to show in the tray icon menu // Checks what to show in the tray icon menu
@ -176,7 +204,7 @@ function createWindow () {
if (minimize) updateTray(); if (minimize) updateTray();
// Unregisters Alt+F4 shortcut // Unregisters Alt+F4 shortcut
globalShortcut.unregister('Alt+F4'); globalShortcut.unregister("Alt+F4");
}); });
win.on("focus", () => { win.on("focus", () => {
@ -184,23 +212,22 @@ function createWindow () {
if (minimize) updateTray(); if (minimize) updateTray();
// Registers shortcut for closing(quitting) the app // Registers shortcut for closing(quitting) the app
globalShortcut.register('Alt+F4', () => safeQuit()); globalShortcut.register("Alt+F4", () => safeQuit());
win.webContents.send('window-is-focused', null); win.webContents.send("window-is-focused", null);
}); });
// Menu bar // Menu bar
win.setAutoHideMenuBar(true); win.setAutoHideMenuBar(true);
win.setMenuBarVisibility(isDevelopment); win.setMenuBarVisibility(isDevelopment);
setMenu(); setMenu();
}
}; function createTray() {
function createTray () {
// Minimize to tray logic follows: // Minimize to tray logic follows:
// Set the tray icon // Set the tray icon
let iconPath; let iconPath;
if (process.platform === 'darwin') { if (process.platform === "darwin") {
// Using @2x for mac retina screens so the icon isn't blurry // 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 // file name needs to include "Template" at the end for dark menu bar
iconPath = path.join(__static, "/img/fav/macTemplate@2x.png"); iconPath = path.join(__static, "/img/fav/macTemplate@2x.png");
@ -211,9 +238,9 @@ function createTray () {
tray = new Tray(iconPath); tray = new Tray(iconPath);
tray.setToolTip("LBRY App"); tray.setToolTip("LBRY App");
tray.setTitle("LBRY"); tray.setTitle("LBRY");
tray.on('double-click', () => { tray.on("double-click", () => {
win.show() win.show();
}) });
} }
// This needs to be done as for linux the context menu doesn't update automatically(docs) // This needs to be done as for linux the context menu doesn't update automatically(docs)
@ -226,27 +253,26 @@ function updateTray() {
} }
} }
function getMenuTemplate () { function getMenuTemplate() {
return [ return [
getToggleItem(), getToggleItem(),
{ {
label: "Quit", label: "Quit",
click: () => safeQuit(), click: () => safeQuit(),
}, },
] ];
function getToggleItem () { function getToggleItem() {
if (win.isVisible() && win.isFocused()) { if (win.isVisible() && win.isFocused()) {
return { return {
label: 'Hide LBRY App', label: "Hide LBRY App",
click: () => win.hide() click: () => win.hide(),
};
}
} else { } else {
return { return {
label: 'Show LBRY App', label: "Show LBRY App",
click: () => win.show() click: () => win.show(),
} };
} }
} }
} }
@ -256,20 +282,19 @@ function handleOpenUriRequested(uri) {
// Window not created yet, so store up requested URI for when it is // Window not created yet, so store up requested URI for when it is
openUri = processRequestedUri(uri); openUri = processRequestedUri(uri);
} else { } else {
if (win.isMinimized()) { if (win.isMinimized()) {
win.restore() win.restore();
} else if (!win.isVisible()) { } else if (!win.isVisible()) {
win.show() win.show();
} }
win.focus(); win.focus();
win.webContents.send('open-uri-requested', processRequestedUri(uri)); win.webContents.send("open-uri-requested", processRequestedUri(uri));
} }
} }
function handleDaemonSubprocessExited() { function handleDaemonSubprocessExited() {
console.log('The daemon has exited.'); console.log("The daemon has exited.");
daemonSubprocess = null; daemonSubprocess = null;
if (!daemonStopRequested) { if (!daemonStopRequested) {
// We didn't request to stop the daemon, so display a // We didn't request to stop the daemon, so display a
@ -277,27 +302,31 @@ function handleDaemonSubprocessExited() {
// //
// TODO: maybe it would be better to restart the daemon? // TODO: maybe it would be better to restart the daemon?
if (win) { if (win) {
console.log('Did not request daemon stop, so quitting in 5 seconds.'); console.log("Did not request daemon stop, so quitting in 5 seconds.");
win.loadURL(`file://${__static}/warning.html`); win.loadURL(`file://${__static}/warning.html`);
setTimeout(quitNow, 5000); setTimeout(quitNow, 5000);
} else { } else {
console.log('Did not request daemon stop, so quitting.'); console.log("Did not request daemon stop, so quitting.");
quitNow(); quitNow();
} }
} }
} }
function launchDaemon() { function launchDaemon() {
assert(!daemonSubprocess, 'Tried to launch daemon twice'); assert(!daemonSubprocess, "Tried to launch daemon twice");
console.log('Launching daemon:', DAEMON_PATH) console.log("Launching daemon:", DAEMON_PATH);
daemonSubprocess = child_process.spawn(DAEMON_PATH) daemonSubprocess = child_process.spawn(DAEMON_PATH);
// Need to handle the data event instead of attaching to // Need to handle the data event instead of attaching to
// process.stdout because the latter doesn't work. I believe on // process.stdout because the latter doesn't work. I believe on
// windows it buffers stdout and we don't get any meaningful output // windows it buffers stdout and we don't get any meaningful output
daemonSubprocess.stdout.on('data', (buf) => {console.log(String(buf).trim());}); daemonSubprocess.stdout.on("data", buf => {
daemonSubprocess.stderr.on('data', (buf) => {console.log(String(buf).trim());}); console.log(String(buf).trim());
daemonSubprocess.on('exit', handleDaemonSubprocessExited); });
daemonSubprocess.stderr.on("data", buf => {
console.log(String(buf).trim());
});
daemonSubprocess.on("exit", handleDaemonSubprocessExited);
} }
/* /*
@ -318,7 +347,7 @@ function quitNow() {
safeQuit(); safeQuit();
} }
const isSecondaryInstance = app.makeSingleInstance((argv) => { const isSecondaryInstance = app.makeSingleInstance(argv => {
if (argv.length >= 2) { if (argv.length >= 2) {
handleOpenUriRequested(argv[1]); // This will handle restoring and focusing the window handleOpenUriRequested(argv[1]); // This will handle restoring and focusing the window
} else if (win) { } else if (win) {
@ -331,25 +360,23 @@ const isSecondaryInstance = app.makeSingleInstance((argv) => {
} }
}); });
if (isSecondaryInstance) { // We're not in the original process, so quit if (isSecondaryInstance) {
// We're not in the original process, so quit
quitNow(); quitNow();
} }
function launchDaemonIfNotRunning() { function launchDaemonIfNotRunning() {
// Check if the daemon is already running. If we get // Check if the daemon is already running. If we get
// an error its because its not running // an error its because its not running
console.log('Checking for lbrynet daemon'); console.log("Checking for lbrynet daemon");
client.request( client.request("status", [], function(err, res) {
'status', [], if (err) {
function (err, res) { console.log("lbrynet daemon needs to be launched");
if (err) { launchDaemon();
console.log('lbrynet daemon needs to be launched') } else {
launchDaemon(); console.log("lbrynet daemon is already running");
} else {
console.log('lbrynet daemon is already running')
}
} }
); });
} }
/* /*
@ -358,21 +385,31 @@ function launchDaemonIfNotRunning() {
* tries to force kill them. * tries to force kill them.
*/ */
function forceKillAllDaemonsAndQuit() { function forceKillAllDaemonsAndQuit() {
console.log('Attempting to force kill any running lbrynet-daemon instances...'); console.log(
"Attempting to force kill any running lbrynet-daemon instances..."
);
const daemonPids = getPidsForProcessName('lbrynet-daemon'); const daemonPids = getPidsForProcessName("lbrynet-daemon");
if (!daemonPids) { if (!daemonPids) {
console.log('No lbrynet-daemon found running.'); console.log("No lbrynet-daemon found running.");
quitNow(); quitNow();
} else { } else {
console.log(`Found ${daemonPids.length} running daemon instances. Attempting to force kill...`); console.log(
`Found ${
daemonPids.length
} running daemon instances. Attempting to force kill...`
);
for (const pid of daemonPids) { for (const pid of daemonPids) {
let daemonKillAttemptsComplete = 0; let daemonKillAttemptsComplete = 0;
kill(pid, 'SIGKILL', (err) => { kill(pid, "SIGKILL", err => {
daemonKillAttemptsComplete++; daemonKillAttemptsComplete++;
if (err) { if (err) {
console.log(`Failed to force kill daemon task with pid ${pid}. Error message: ${err.message}`); console.log(
`Failed to force kill daemon task with pid ${pid}. Error message: ${
err.message
}`
);
} else { } else {
console.log(`Force killed daemon task with pid ${pid}.`); console.log(`Force killed daemon task with pid ${pid}.`);
} }
@ -384,15 +421,15 @@ function forceKillAllDaemonsAndQuit() {
} }
} }
app.setAsDefaultProtocolClient('lbry'); app.setAsDefaultProtocolClient("lbry");
app.on('ready', function() { app.on("ready", function() {
launchDaemonIfNotRunning(); launchDaemonIfNotRunning();
if (process.platform === "linux") { if (process.platform === "linux") {
checkLinuxTraySupport( err => { checkLinuxTraySupport(err => {
if (!err) createTray(); if (!err) createTray();
else minimize = false; else minimize = false;
}) });
} else { } else {
createTray(); createTray();
} }
@ -400,36 +437,35 @@ app.on('ready', function() {
}); });
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', () => { app.on("window-all-closed", () => {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== "darwin") {
app.quit() app.quit();
} }
}) });
app.on("before-quit", event => {
app.on('before-quit', (event) => {
if (!readyToQuit) { if (!readyToQuit) {
// We need to shutdown the daemon before we're ready to actually quit. This // 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 will be triggered re-entrantly once preparation is done.
event.preventDefault(); event.preventDefault();
shutdownDaemonAndQuit(); shutdownDaemonAndQuit();
} else { } else {
console.log('Quitting.') console.log("Quitting.");
} }
}); });
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 (win === null) { if (win === null) {
createWindow() createWindow();
} }
}); });
if (process.platform === 'darwin') { if (process.platform === "darwin") {
app.on('open-url', (event, uri) => { app.on("open-url", (event, uri) => {
handleOpenUriRequested(uri); handleOpenUriRequested(uri);
}); });
} else if (process.argv.length >= 2) { } else if (process.argv.length >= 2) {
@ -440,14 +476,18 @@ if (process.platform === 'darwin') {
// then calls quitNow() to quit for real. // then calls quitNow() to quit for real.
function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) { function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
function doShutdown() { function doShutdown() {
console.log('Shutting down daemon'); console.log("Shutting down daemon");
daemonStopRequested = true; daemonStopRequested = true;
client.request('daemon_stop', [], (err, res) => { client.request("daemon_stop", [], (err, res) => {
if (err) { if (err) {
console.log(`received error when stopping lbrynet-daemon. Error message: ${err.message}\n`); console.log(
console.log('You will need to manually kill the daemon.'); `received error when stopping lbrynet-daemon. Error message: ${
err.message
}\n`
);
console.log("You will need to manually kill the daemon.");
} else { } else {
console.log('Successfully stopped daemon via RPC call.') console.log("Successfully stopped daemon via RPC call.");
quitNow(); quitNow();
} }
}); });
@ -456,7 +496,7 @@ function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
if (daemonSubprocess) { if (daemonSubprocess) {
doShutdown(); doShutdown();
} else if (!evenIfNotStartedByApp) { } else if (!evenIfNotStartedByApp) {
console.log('Not killing lbrynet-daemon because app did not start it'); console.log("Not killing lbrynet-daemon because app did not start it");
quitNow(); quitNow();
} else { } else {
doShutdown(); doShutdown();
@ -467,24 +507,27 @@ function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
} }
// Taken from webtorrent-desktop // Taken from webtorrent-desktop
function checkLinuxTraySupport (cb) { function checkLinuxTraySupport(cb) {
// Check that we're on Ubuntu (or another debian system) and that we have // Check that we're on Ubuntu (or another debian system) and that we have
// libappindicator1. // libappindicator1.
child_process.exec('dpkg --get-selections libappindicator1', function (err, stdout) { child_process.exec("dpkg --get-selections libappindicator1", function(
if (err) return cb(err) err,
stdout
) {
if (err) return cb(err);
// Unfortunately there's no cleaner way, as far as I can tell, to check // Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed: // whether a debian package is installed:
if (stdout.endsWith('\tinstall\n')) { if (stdout.endsWith("\tinstall\n")) {
cb(null) cb(null);
} else { } else {
cb(new Error('debian package not installed')) cb(new Error("debian package not installed"));
} }
}) });
} }
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); openItem(installerPath);
@ -497,58 +540,77 @@ ipcMain.on('upgrade', (event, installerPath) => {
shutdownDaemonAndQuit(true); shutdownDaemonAndQuit(true);
// wait for daemon to shut down before upgrading // 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('The app will close, and you will be prompted to install the latest version of LBRY.'); console.log(
console.log('After the install is complete, please reopen the app.'); "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.");
}); });
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 = ''; let result = "";
const opts = { const opts = {
headers: { headers: {
'User-Agent': `LBRY/${localVersion}`, "User-Agent": `LBRY/${localVersion}`,
} },
}; };
const req = https.get(Object.assign(opts, url.parse(LATEST_RELEASE_API_URL)), (res) => { const req = https.get(
res.on('data', (data) => { Object.assign(opts, url.parse(LATEST_RELEASE_API_URL)),
result += data; res => {
}); res.on("data", data => {
res.on('end', () => { result += data;
const tagName = JSON.parse(result).tag_name; });
const [_, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/); res.on("end", () => {
if (!remoteVersion) { const tagName = JSON.parse(result).tag_name;
if (win) { const [_, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
win.webContents.send('version-info-received', null); if (!remoteVersion) {
if (win) {
win.webContents.send("version-info-received", null);
}
} else {
const upgradeAvailable = semver.gt(
formatRc(remoteVersion),
formatRc(localVersion)
);
if (win) {
win.webContents.send("version-info-received", {
remoteVersion,
localVersion,
upgradeAvailable,
});
}
} }
} else { });
const upgradeAvailable = semver.gt(formatRc(remoteVersion), formatRc(localVersion)); }
if (win) { );
win.webContents.send('version-info-received', {remoteVersion, localVersion, upgradeAvailable});
}
}
})
});
req.on('error', (err) => { req.on("error", err => {
console.log('Failed to get current version from GitHub. Error:', err); console.log("Failed to get current version from GitHub. Error:", err);
if (win) { if (win) {
win.webContents.send('version-info-received', null); win.webContents.send("version-info-received", null);
} }
}); });
}); });
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
);
}); });

View file

@ -1,6 +1,9 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { doUserEmailVerify } from "redux/actions/user"; import {
doUserEmailVerify,
doUserEmailVerifyFailure,
} from "redux/actions/user";
import { import {
selectEmailVerifyIsPending, selectEmailVerifyIsPending,
selectEmailToVerify, selectEmailToVerify,
@ -20,7 +23,9 @@ const select = state => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
verifyUserEmail: code => dispatch(doUserEmailVerify(code)), verifyUserEmail: (code, recaptcha) =>
dispatch(doUserEmailVerify(code, recaptcha)),
verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)),
}); });
export default connect(select, perform)(UserEmailVerify); export default connect(select, perform)(UserEmailVerify);

View file

@ -20,7 +20,12 @@ class UserEmailVerify extends React.PureComponent {
handleSubmit() { handleSubmit() {
const { code } = this.state; const { code } = this.state;
this.props.verifyUserEmail(code); try {
let verification = JSON.parse(atob(code));
this.props.verifyUserEmail(verification.token, verification.recaptcha);
} catch (error) {
this.props.verifyUserEmailFailure("Invalid Verification Token");
}
} }
render() { render() {

View file

@ -5,8 +5,13 @@ import SnackBar from "component/snackBar";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import store from "store.js"; import store from "store.js";
import SplashScreen from "component/splash"; import SplashScreen from "component/splash";
import { doDaemonReady } from "redux/actions/app"; import {
doDaemonReady,
doShowSnackBar,
doConditionalAuthNavigate,
} from "redux/actions/app";
import { doNavigate } from "redux/actions/navigation"; import { doNavigate } from "redux/actions/navigation";
import { doUserEmailVerify } from "redux/actions/user";
import { doDownloadLanguages } from "redux/actions/settings"; import { doDownloadLanguages } from "redux/actions/settings";
import * as types from "constants/action_types"; import * as types from "constants/action_types";
import amplitude from "amplitude-js"; import amplitude from "amplitude-js";
@ -37,9 +42,26 @@ window.addEventListener("contextmenu", event => {
event.preventDefault(); event.preventDefault();
}); });
ipcRenderer.on("open-uri-requested", (event, uri) => { ipcRenderer.on("open-uri-requested", (event, uri, newSession) => {
if (uri && uri.startsWith("lbry://")) { if (uri && uri.startsWith("lbry://")) {
app.store.dispatch(doNavigate("/show", { uri })); if (uri.startsWith("lbry://?verify=")) {
let verification = {};
try {
verification = JSON.parse(atob(uri.substring(15)));
} catch (error) {}
if (verification.token && verification.recaptcha) {
app.store.dispatch(doConditionalAuthNavigate(newSession));
app.store.dispatch(
doUserEmailVerify(verification.token, verification.recaptcha)
);
} else {
app.store.dispatch(
doShowSnackBar({ message: "Invalid Verification URI" })
);
}
} else {
app.store.dispatch(doNavigate("/show", { uri }));
}
} }
}); });

View file

@ -7,6 +7,7 @@ import {
selectUpgradeFilename, selectUpgradeFilename,
selectIsUpgradeSkipped, selectIsUpgradeSkipped,
selectRemoteVersion, selectRemoteVersion,
selectCurrentModal,
} from "redux/selectors/app"; } from "redux/selectors/app";
import { doFetchDaemonSettings } from "redux/actions/settings"; import { doFetchDaemonSettings } from "redux/actions/settings";
import { doBalanceSubscribe } from "redux/actions/wallet"; import { doBalanceSubscribe } from "redux/actions/wallet";
@ -14,8 +15,7 @@ import { doAuthenticate } from "redux/actions/user";
import { doFetchFileInfosAndPublishedClaims } from "redux/actions/file_info"; import { doFetchFileInfosAndPublishedClaims } from "redux/actions/file_info";
import * as modals from "constants/modal_types"; import * as modals from "constants/modal_types";
import { doFetchRewardedContent } from "redux/actions/content"; import { doFetchRewardedContent } from "redux/actions/content";
import { selectCurrentModal } from "redux/selectors/app"; import { doAuthNavigate } from "redux/actions/navigation";
const { remote, ipcRenderer, shell } = require("electron"); const { remote, ipcRenderer, shell } = require("electron");
const path = require("path"); const path = require("path");
const { download } = remote.require("electron-dl"); const { download } = remote.require("electron-dl");
@ -266,3 +266,12 @@ export function doChangeVolume(volume) {
}); });
}; };
} }
export function doConditionalAuthNavigate(newSession) {
return function(dispatch, getState) {
const state = getState();
if (newSession || selectCurrentModal(state) !== "email_collection") {
dispatch(doAuthNavigate());
}
};
}

View file

@ -68,14 +68,14 @@ export function doUserEmailNew(email) {
data: { email }, data: { email },
}); });
dispatch(doUserFetch()); dispatch(doUserFetch());
} };
const failure = error => { const failure = error => {
dispatch({ dispatch({
type: types.USER_EMAIL_NEW_FAILURE, type: types.USER_EMAIL_NEW_FAILURE,
data: { error }, data: { error },
}); });
} };
lbryio lbryio
.call( .call(
@ -86,12 +86,14 @@ export function doUserEmailNew(email) {
) )
.catch(error => { .catch(error => {
if (error.response && error.response.status == 409) { if (error.response && error.response.status == 409) {
return lbryio.call( return lbryio
"user_email", .call(
"resend_token", "user_email",
{ email: email, only_if_expired: true }, "resend_token",
"post" { email: email, only_if_expired: true },
).then(success, failure); "post"
)
.then(success, failure);
} }
throw error; throw error;
}) })
@ -99,21 +101,25 @@ export function doUserEmailNew(email) {
}; };
} }
export function doUserEmailVerify(verificationToken) { export function doUserEmailVerify(verificationToken, recaptcha) {
return function(dispatch, getState) { return function(dispatch, getState) {
const email = selectEmailToVerify(getState()); const email = selectEmailToVerify(getState());
verificationToken = verificationToken.toString().trim();
dispatch({ dispatch({
type: types.USER_EMAIL_VERIFY_STARTED, type: types.USER_EMAIL_VERIFY_STARTED,
code: verificationToken, code: verificationToken,
recaptcha: recaptcha,
}); });
lbryio lbryio
.call( .call(
"user_email", "user_email",
"confirm", "confirm",
{ verification_token: verificationToken, email: email }, {
verification_token: verificationToken,
email: email,
recaptcha: recaptcha,
},
"post" "post"
) )
.then(userEmail => { .then(userEmail => {
@ -128,12 +134,13 @@ export function doUserEmailVerify(verificationToken) {
throw new Error("Your email is still not verified."); //shouldn't happen throw new Error("Your email is still not verified."); //shouldn't happen
} }
}) })
.catch(error => { .catch(error => dispatch(doUserEmailVerifyFailure(error)));
dispatch({ };
type: types.USER_EMAIL_VERIFY_FAILURE, }
data: { error }, export function doUserEmailVerifyFailure(error) {
}); return {
}); type: types.USER_EMAIL_VERIFY_FAILURE,
data: { error },
}; };
} }