Electron, Flux + React

30
Electron, Flux + React Tom Moor (@tommoor)

Transcript of Electron, Flux + React

Page 1: Electron, Flux + React

Electron, Flux + ReactTom Moor (@tommoor)

Page 2: Electron, Flux + React

What is Speak?Instant audio, video and screen share for remote teams

Page 3: Electron, Flux + React

A Quick DemoWhat could go wrong?

Page 4: Electron, Flux + React

App & Dock

Page 5: Electron, Flux + React

Ring ring...

Page 6: Electron, Flux + React

Audio & Video

Page 7: Electron, Flux + React

Why Electron?!Are we sure this is a good idea...

Page 8: Electron, Flux + React

Chromi ummmmCross platform full stack AV implementation

WebRTC as a first class citizen, always being improved upstream

NodeAccess to underlying system

SpeedLean startup, MVP

Known technologies, CSS & Javascript, NW.js

Cross platform a must for communication tools

Page 9: Electron, Flux + React

Our ArchitectureIt’s events all the way down

Page 10: Electron, Flux + React

MicroservicesRabbitMQ message bus

Distinct services for calls, authentication, websockets, audio mixing, api…

Mixture of Ruby and Go languages

Realtime APIEvents with payloads over websockets

Transactions for responses

Page 11: Electron, Flux + React

The ClientElectron, Flux & React

Page 12: Electron, Flux + React

Flux Data FlowUnidirectional data flow

Action Dispatcher Store View

Page 13: Electron, Flux + React

Flux Data FlowUnidirectional data flow

Action Dispatcher Store View

Libs

Page 14: Electron, Flux + React

DockMainCall

Node Process

Page 15: Electron, Flux + React

RenderersMain, Dock, Call, (Preferences, Teams, Invites …)

Each renderer has it’s own React entry point

Each renderer has it’s own Flux Dispatcher and copy of all stores

Events are forwarded bi-directionally through IPC

Main RendererWe have one main renderer that communicates with server, renders audio etc

Because complex objects can’t be passed over IPC

Ensure this is never closed, only hidden

Page 16: Electron, Flux + React

The lifetime of an eventdata flow with multiple renderers

Page 17: Electron, Flux + React

User clicksdock renderer user-

menu.jsx

var React = require('react/addons');

var UserActions = require('../actions/user-actions');

var UserMenu = React.createClass({

call: function() {

UserActions.call(this.props.item);

},

render: function(){

return <ul className="actions user-actions">

<li key="call"><a onClick={this.call} className="positive"><i className="

icon-call"></i></a></li>

</ul>;

}

});

module.exports = UserMenu;

Page 18: Electron, Flux + React

Event Dispatched

dock renderer

user-actions.js

var AppDispatcher = require('../dispatcher/app-dispatcher');

var UserStore = require('../stores/user-store');

var UserActions = {

call: function(user) {

AppDispatcher.dispatch('channel.invite', {

user_id: user.id,

sender_id: UserStore.get('id')

});

}

...

};

module.exports = UserActions;

Page 19: Electron, Flux + React

IPC Senddock renderer

app-dispatcher.js

var ipc = require('ipc');

var browserWindow = require('remote').getCurrentWindow();

var AppDispatcher = Flux.createDispatcher({...});

// receive events from main process

ipc.on('event', function(action, payload, opt, browser_id) {

if (browser_id != browserWindow.id) {

AppDispatcher.dispatch(action, payload, opt, browser_id);

}

});

// send events from this renderer to main process

AppDispatcher.register(function(action, payload, opt, browser_id) {

if (!browser_id) {

ipc.send('event', action, payload, opt, browserWindow.id);

}

});

module.exports = AppDispatcher;

Page 20: Electron, Flux + React

IPC Receivenode process

dispatcher.js

class AppDispatcher {

constructor(windows) {

ipc.on('event', function(ev, action, payload, opts, browser_id){

this.dispatch(action, payload, opts, browser_id);

}.bind(this));

}

dispatch(action, payload, opts, browser_id) {

browser_id = browser_id || 'node';

_.each(global.application.windows, function(window, window_id){

if (window_id != browser_id) {

window.browserWindow.webContents.send('event', action,

payload, opts, browser_id);

}

});

}

}

Page 21: Electron, Flux + React

IPC Receivemain renderer

app-dispatcher.js

var ipc = require('ipc');

var browserWindow = require('remote').getCurrentWindow();

var AppDispatcher = Flux.createDispatcher({...});

// receive events from main process

ipc.on('event', function(action, payload, opt, browser_id) {

if (browser_id != browserWindow.id) {

AppDispatcher.dispatch(action, payload, opt, browser_id);

}

});

// send events from this renderer to main process

AppDispatcher.register(function(action, payload, opt, browser_id)

{

if (!browser_id) {

ipc.send('event', action, payload, opt, browserWindow.id);

}

});

module.exports = AppDispatcher;

Page 22: Electron, Flux + React

WebSocketsmain renderer

socks.js

var Socks = {

actions: {

'channel.invite': 'send',

...

},

send: function(action, params, options) {

if (this.ws && this.ws.readyState === WebSocket.OPEN) {

var data = {key: action};

if (params) data.params = params;

if (options) data.transaction_id = options;

this.ws.send(JSON.stringify(data));

return true;

}

}

};

module.exports = Socks;

Page 23: Electron, Flux + React

What about new windows?Synchronising the state of the system

Page 24: Electron, Flux + React

Local storage to the rescue!Automatically in sync between pages on the same domain

Page 25: Electron, Flux + React

Extend the flux store

var Store = function(definition) {

return Flux.createStore(_.extend({

initialize: function() {

this.rehydrateStore();

this.onChange(function(){

LocalStorage.set(this.storeName, this.state);

}.bind(this));

},

rehydrateStore: function() {

var data = LocalStorage.get(this.storeName);

if (data) {

this.state = _.extend(this.state, data);

this.emit('change');

}

}

}, definition));

};

Using Delorean

Page 26: Electron, Flux + React

ChallengesThink within the box

Page 27: Electron, Flux + React

DesignWe come up with a lot of wild and wacky ideas…

Build on the advantages of Electron, not the disadvantages

Don’t dupe native styles and controls if possible

Complex objects limit designs (e.g streams)

Page 28: Electron, Flux + React

PerformanceJavascript is FAST

IPC is slow, avoid at all costs

Starting windows is slow, we keep some open and hidden (hibernate?)

Each renderer and crash reporter is a process (it adds up quickly!)

Page 29: Electron, Flux + React

Windows & LinuxCare of platform differences, conventions, wording

Chromium still renders css differently occasionally

Updates for each platform are quite different

Virtual machines, VMWare for developing on Windows

NPM3 for long file paths

Github for Windows recommended!

Page 30: Electron, Flux + React

Questions?speak.io / @speak_io / @tommoor