parityStore.js 3.81 KiB
Newer Older
Amaury Martiny's avatar
Amaury Martiny committed
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
Amaury Martiny's avatar
Amaury Martiny committed
// SPDX-License-Identifier: BSD-3-Clause
Amaury Martiny's avatar
Amaury Martiny committed

import { action, observable } from 'mobx';
import Api from '@parity/api';
Amaury Martiny's avatar
Amaury Martiny committed
import isElectron from 'is-electron';
import light from '@parity/light.js';
import { distinctUntilChanged, map } from 'rxjs/operators';
Amaury Martiny's avatar
Amaury Martiny committed
import store from 'store';
Axel Chalon's avatar
Axel Chalon committed
import { timer } from 'rxjs';
Amaury Martiny's avatar
Amaury Martiny committed

import Debug from '../utils/debug';
Amaury Martiny's avatar
Amaury Martiny committed
import LS_PREFIX from './utils/lsPrefix';

Amaury Martiny's avatar
Amaury Martiny committed
const debug = Debug('parityStore');
Amaury Martiny's avatar
Amaury Martiny committed
const electron = isElectron() ? window.require('electron') : null;

Amaury Martiny's avatar
Amaury Martiny committed
const LS_KEY = `${LS_PREFIX}::secureToken`;
Amaury Martiny's avatar
Amaury Martiny committed

Amaury Martiny's avatar
Amaury Martiny committed
export class ParityStore {
Axel Chalon's avatar
Axel Chalon committed
  // TODO This is not working
  // api.on('connected', () => ...);
  // api.on('disconnected', () => ...);
  // So instead, we poll every 1s
Axel Chalon's avatar
Axel Chalon committed
  isApiConnected$ = timer(0, 1000).pipe(
    map(_ => Boolean(this.api && this.api.isConnected)),
Axel Chalon's avatar
Axel Chalon committed
    distinctUntilChanged()
Axel Chalon's avatar
Axel Chalon committed
  );
Axel Chalon's avatar
Axel Chalon committed

Axel Chalon's avatar
Axel Chalon committed
  @observable
  isParityRunning = false;
Axel Chalon's avatar
Axel Chalon committed

Axel Chalon's avatar
Axel Chalon committed
  @observable
  token = null;
Amaury Martiny's avatar
Amaury Martiny committed

  @observable
  api = undefined;

Amaury Martiny's avatar
Amaury Martiny committed
  constructor () {
Amaury Martiny's avatar
Amaury Martiny committed
    // Retrieve token from localStorage
    const token = store.get(LS_KEY);
    if (token) {
Amaury Martiny's avatar
Amaury Martiny committed
      debug('Got token from localStorage.');
Amaury Martiny's avatar
Amaury Martiny committed
      this.setToken(token);
    }

    // FIXME - consider moving to start of this constructor block since
    // if `setToken` method is called then `connectToApi` is called, which
    // requires `electron` to be defined
Amaury Martiny's avatar
Amaury Martiny committed
    if (!electron) {
Amaury Martiny's avatar
Amaury Martiny committed
      debug(
Amaury Martiny's avatar
Amaury Martiny committed
        'Not in Electron, ParityStore will only have limited capabilities.'
      );
      return;
    }

    const { ipcRenderer, remote } = electron;

    // Check if isParityRunning
    this.setIsParityRunning(!!remote.getGlobal('isParityRunning'));
    // We also listen to future changes
    ipcRenderer.on('parity-running', (_, isParityRunning) => {
      this.setIsParityRunning(isParityRunning);
    });
  }

  connectToApi = () => {
Amaury Martiny's avatar
Amaury Martiny committed
    // Get the provider, optionally from --ws-interface and --ws-port flags
    const [defaultInterface, defaultPort] = ['127.0.0.1', '8546'];
    let provider = `ws://${defaultInterface}:${defaultPort}`;
Amaury Martiny's avatar
Amaury Martiny committed
    if (electron) {
      const { remote } = electron;
      const wsInterface = remote.getGlobal('wsInterface');
      const wsPort = remote.getGlobal('wsPort');
Amaury Martiny's avatar
Amaury Martiny committed
      provider = `ws://${wsInterface || defaultInterface}:${wsPort ||
        defaultPort}`;
Amaury Martiny's avatar
Amaury Martiny committed
    }

Amaury Martiny's avatar
Amaury Martiny committed
    debug(`Connecting to ${provider}.`);
Amaury Martiny's avatar
Amaury Martiny committed
    const api = new Api(
      new Api.Provider.Ws(
        provider,
        this.token.replace(/[^a-zA-Z0-9]/g, '') // Sanitize token
      )
    );

    // Initialize the light.js lib
    light.setApi(api);

    // Also set api as member for React Components to use it if needed
    this.api = api;
  };

  requestNewToken = () => {
    const { ipcRenderer } = electron;

    // Request new token from Electron
Amaury Martiny's avatar
Amaury Martiny committed
    debug('Requesting new token.');
Amaury Martiny's avatar
Amaury Martiny committed
    ipcRenderer.send('asynchronous-message', 'signer-new-token');
    ipcRenderer.once('signer-new-token-reply', (_, token) => {
Amaury Martiny's avatar
Amaury Martiny committed
      if (!token) {
        return;
      }
      // If `parity signer new-token` has successfully given us a token back,
      // then we submit it
Amaury Martiny's avatar
Amaury Martiny committed
      debug('Successfully received new token.');
Amaury Martiny's avatar
Amaury Martiny committed
      this.setToken(token);
    });
  };

  @action
  setIsParityRunning = isParityRunning => {
    if (isParityRunning === this.isParityRunning) {
      return;
    }

    this.isParityRunning = isParityRunning;

    // Request new token if parity's running but we still don't have a token
    if (isParityRunning && !this.token) {
      this.requestNewToken();
    }
  };

  @action
  setToken = token => {
    if (token === this.token) {
      return;
    }

    this.token = token;

    // If we receive a new token, then we try to connect to the Api with this
    // new token
    this.connectToApi();

    this.updateLS();
  };

  updateLS = () => store.set(LS_KEY, this.token);
}

export default new ParityStore();