diff options
| -rw-r--r-- | app/app.js | 14 | ||||
| -rw-r--r-- | app/lib/backend-redux-actions.js | 83 | ||||
| -rw-r--r-- | app/lib/backend-routing.js | 30 | ||||
| -rw-r--r-- | app/lib/backend.js | 44 | ||||
| -rw-r--r-- | app/lib/ipc-facade.js | 9 | ||||
| -rw-r--r-- | test/mocks/ipc.js | 2 | ||||
| -rw-r--r-- | test/routing.spec.js | 37 |
7 files changed, 18 insertions, 201 deletions
diff --git a/app/app.js b/app/app.js index 07c3126fa1..1e56830880 100644 --- a/app/app.js +++ b/app/app.js @@ -13,8 +13,6 @@ import configureStore from './redux/store'; import accountActions from './redux/account/actions'; import connectionActions from './redux/connection/actions'; import { Backend } from './lib/backend'; -import mapBackendEventsToReduxActions from './lib/backend-redux-actions'; -import mapBackendEventsToRouter from './lib/backend-routing'; import type { LoginState } from './redux/account/reducers'; import type { ConnectionState } from './redux/connection/reducers'; @@ -57,23 +55,15 @@ const getIconType = (s: ConnectionState): TrayIconType => { */ const updateTrayIcon = () => { const { connection } = store.getState(); + // TODO: Only update the tray icon if the connection status changed ipcRenderer.send('changeTrayIcon', getIconType(connection.status)); }; - -// Setup primary event handlers to translate backend events into redux dispatch -mapBackendEventsToReduxActions(backend, store); - -// Setup routing based on backend events -mapBackendEventsToRouter(backend, store); +store.subscribe(updateTrayIcon); ipcRenderer.on('backend-info', (_event, args) => { backend.setLocation(args.addr); backend.sync(); }); -// Setup events to update tray icon -backend.on('connect', updateTrayIcon); -backend.on('connecting', updateTrayIcon); -backend.on('disconnect', updateTrayIcon); // force update tray updateTrayIcon(); diff --git a/app/lib/backend-redux-actions.js b/app/lib/backend-redux-actions.js deleted file mode 100644 index bf5f2cc59d..0000000000 --- a/app/lib/backend-redux-actions.js +++ /dev/null @@ -1,83 +0,0 @@ -// @flow - -import log from 'electron-log'; -import accountActions from '../redux/account/actions.js'; -import connectionActions from '../redux/connection/actions.js'; -import { Backend } from './backend.js'; -import type { ReduxStore } from '../redux/store.js'; - -/** - * Add event listeners to translate backend events to redux dispatch. - * - * @export - * @param {Backend} backend - * @param {Redux.Store} store - */ -export default function mapBackendEventsToReduxActions(backend: Backend, store: ReduxStore) { - - const onUpdateIp = (clientIp) => { - store.dispatch(connectionActions.connectionChange({ clientIp })); - }; - - const onUpdateLocation = (data) => { - store.dispatch(accountActions.loginChange(data)); - }; - - const onConnecting = (serverAddress) => { - store.dispatch(connectionActions.connectionChange({ - status: 'connecting', - serverAddress - })); - }; - - const onConnect = (serverAddress, error) => { - if (error) { - log.error('Unable to connect to', serverAddress, error); - } else { - store.dispatch(connectionActions.connectionChange({ status: 'connected' })); - } - }; - - const onDisconnect = () => { - store.dispatch(connectionActions.connectionChange({ - status: 'disconnected', - serverAddress: null - })); - }; - - const onLoggingIn = (info) => { - store.dispatch(accountActions.loginChange(Object.assign({ - status: 'connecting', - error: null - }, info))); - }; - - const onLogin = (info, error) => { - const status = error ? 'failed' : 'ok'; - const paidUntil = info.paidUntil ? info.paidUntil : null; - store.dispatch(accountActions.loginChange({ paidUntil, status, error })); - }; - - const onLogout = () => { - store.dispatch(accountActions.loginChange({ - status: 'none', - accountNumber: '', - paidUntil: null, - error: null - })); - }; - - const onReachability = (isOnline) => { - store.dispatch(connectionActions.connectionChange({ isOnline })); - }; - - backend.on('updatedIp', onUpdateIp); - backend.on('updatedLocation', onUpdateLocation); - backend.on('connecting', onConnecting); - backend.on('connect', onConnect); - backend.on('disconnect', onDisconnect); - backend.on('logging', onLoggingIn); - backend.on('login', onLogin); - backend.on('logout', onLogout); - backend.on('updatedReachability', onReachability); -} diff --git a/app/lib/backend-routing.js b/app/lib/backend-routing.js deleted file mode 100644 index 2a49a8a3e9..0000000000 --- a/app/lib/backend-routing.js +++ /dev/null @@ -1,30 +0,0 @@ -import { replace } from 'react-router-redux'; - -/** - * Add listeners to translate backend events to react router actions - * - * @export - * @param {Backend} backend - * @param {Redux.Store} store - */ -export default function mapBackendEventsToRouter(backend, store) { - // redirect user to main screen after login - backend.on('login', (_account, error) => { - if(error) { return; } // no-op on error - - setTimeout(() => { - const { settings } = store.getState(); - - // auto-connect only if autoSecure is on - if(settings.autoSecure) { - const server = backend.serverInfo(settings.preferredServer); - backend.connect(server.address); - } - - store.dispatch(replace('/connect')); - }, 1000); - }); - - // redirect user to login page on logout - backend.on('logout', () => store.dispatch(replace('/'))); -} diff --git a/app/lib/backend.js b/app/lib/backend.js index da2bc60613..9c5b2ed064 100644 --- a/app/lib/backend.js +++ b/app/lib/backend.js @@ -94,7 +94,7 @@ export class Backend { this._ipc.getIp() .then( ip => { log.info('Got ip', ip); - this._emit('updatedIp', ip); + this._store.dispatch(connectionActions.connectionChange({ clientIp: ip })); }) .catch(e => { log.info('Failed syncing with the backend', e); @@ -108,7 +108,7 @@ export class Backend { country: location.country, city: location.city }; - this._emit('updatedLocation', newLocation, null); + this._store.dispatch(accountActions.loginChange(newLocation)); }) .catch(e => { log.info('Failed getting new location', e); @@ -147,9 +147,6 @@ export class Backend { login(accountNumber: string) { log.info('Attempting to login with account number', accountNumber); - // emit: logging in - this._emit('logging', { accountNumber: accountNumber }, null); - this._store.dispatch(accountActions.loginChange({ accountNumber: accountNumber, status: 'connecting', @@ -166,10 +163,6 @@ export class Backend { }).then( accountData => { log.info('Log in complete'); - this._emit('login', { - paidUntil: accountData.paid_until, - }, undefined); - this._store.dispatch(accountActions.loginChange({ status: 'ok', paidUntil: accountData.paid_until, @@ -190,8 +183,6 @@ export class Backend { status: 'failed', error: err, })); - - this._emit('login', {}, err); }); } @@ -200,8 +191,6 @@ export class Backend { // @TODO: What does it mean for a logout to be successful or failed? this._ipc.setAccount('') .then(() => { - // emit event - this._emit('logout'); this._store.dispatch(accountActions.loginChange({ status: 'none', @@ -222,8 +211,6 @@ export class Backend { connect(addr: string) { - // emit: connecting - this._emit('connecting', addr); this._store.dispatch(connectionActions.connectionChange({ status: 'connecting', serverAddress: addr, @@ -236,7 +223,6 @@ export class Backend { }) .catch(e => { log.info('Failed connecting to', addr, e); - this._emit('connect', undefined, e); this._store.dispatch(connectionActions.connectionChange({ status: 'disconnected', })); @@ -257,15 +243,19 @@ export class Backend { * with proper backend integration. */ _startReachability() { - window.addEventListener('online', () => this._emit('updatedReachability', true)); + window.addEventListener('online', () => { + this._store.dispatch(connectionActions.connectionChange({ isOnline: true })); + }); window.addEventListener('offline', () => { // force disconnect since there is no real connection anyway. this.disconnect(); - this._emit('updatedReachability', false); + this._store.dispatch(connectionActions.connectionChange({ isOnline: false })); }); // update online status in background - setTimeout(() => this._emit('updatedReachability', navigator.onLine), 0); + setTimeout(() => { + this._store.dispatch(connectionActions.connectionChange({ isOnline: navigator.onLine })); + }, 0); } _registerIpcListeners() { @@ -292,20 +282,4 @@ export class Backend { throw new Error('Unknown backend state: ' + backendState); } } - - on(event: EventType, listener: Function) { - this._eventEmitter.on(event, listener); - } - - once(event: EventType, listener: Function) { - this._eventEmitter.once(event, listener); - } - - off(event: EventType, listener: Function) { - this._eventEmitter.removeListener(event, listener); - } - - _emit(event: EventType, ...args:Array<any>): boolean { - return this._eventEmitter.emit(event, ...args); - } } diff --git a/app/lib/ipc-facade.js b/app/lib/ipc-facade.js index 19daf71b97..ff2b7050ed 100644 --- a/app/lib/ipc-facade.js +++ b/app/lib/ipc-facade.js @@ -1,14 +1,16 @@ // @flow import JsonRpcWs, { InvalidReply } from './jsonrpc-ws-ipc'; -import { object, string, number, arrayOf } from 'validated/schema'; +import { object, string, arrayOf, number } from 'validated/schema'; import { validate } from 'validated/object'; +import type { Coordinate2d } from '../types'; + export type AccountData = {paid_until: string}; export type AccountNumber = string; export type Ip = string; export type Location = { - latlong: Array<number>, + latlong: Coordinate2d, country: string, city: string, }; @@ -90,7 +92,8 @@ export class RealIpc implements IpcFacade { return this._ipc.send('get_location') .then(raw => { try { - return validate(LocationSchema, raw); + const validated: any = validate(LocationSchema, raw); + return (validated: Location); } catch (e) { throw new InvalidReply(raw, e); } diff --git a/test/mocks/ipc.js b/test/mocks/ipc.js index 3225fe9ed9..a172bef8e5 100644 --- a/test/mocks/ipc.js +++ b/test/mocks/ipc.js @@ -37,7 +37,7 @@ export function newMockIpc() { return new Promise(r => r({ city: '', country: '', - latlong: [], + latlong: [0, 0], })); }, getState: () => { diff --git a/test/routing.spec.js b/test/routing.spec.js deleted file mode 100644 index ccb702de8b..0000000000 --- a/test/routing.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -// @flow - -import { expect } from 'chai'; - -import { filterMinorActions, mockState, mockStore } from './mocks/redux'; -import accountActions from '../app/redux/account/actions'; -import mapBackendEventsToRouter from '../app/lib/backend-routing'; -import { Backend } from '../app/lib/backend'; -import { newMockIpc } from './mocks/ipc'; - -describe('routing', function() { - this.timeout(10000); - - it('should redirect to login screen on logout', () => { - const expectedActions = [ - { type: '@@router/CALL_HISTORY_METHOD', payload: { method: 'replace', args: [ '/' ] } } - ]; - - let state = Object.assign(mockState(), { - account: { - account: '1111234567890', - status: 'ok' - } - }); - - const store = mockStore(state); - const backend = new Backend(store, newMockIpc()); - mapBackendEventsToRouter(backend, store); - - store.dispatch(accountActions.logout(backend)); - - setTimeout(() => { - const storeActions = filterMinorActions(store.getActions()); - expect(storeActions).deep.equal(expectedActions); - }, 0); - }); -}); |
