-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor Electron's main process #951
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't seem like this event is actually sent on the renderer side. Did you mean to build that, or is it something we're going to add later? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's necessary to handle it: https://electronjs.org/docs/api/web-contents#event-crashed |
||
window = null; | ||
}); | ||
|
||
return window; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be a whole object?
create()
is only called once and it doesn't need any state, so maybe just acreateTray
function that takes anupdateAttachedWindow
callback?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went for a exported
createTray()
function initially. However, since it also needs to manage the state of the window, I finally went for using a class.