// Copyright 2015-2017 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . import { action, computed, observable, transaction } from 'mobx'; import localStore from 'store'; import { parse as parseUrl } from 'url'; import { encodePath, encodeUrl } from './dapplink'; const DEFAULT_URL = 'https://oasisdex.com'; const LS_LAST_ADDRESS = '_parity::webLastAddress'; const hasProtocol = /^https?:\/\//; let instance = null; export default class Store { @observable counter = Date.now(); @observable currentUrl = null; @observable history = []; @observable isLoading = false; @observable parsedUrl = null; @observable nextUrl = null; @observable token = null; constructor (api) { this._api = api; this.nextUrl = this.currentUrl = this.loadLastUrl(); } @computed get encodedPath () { return `${this._api.dappsUrl}/web/${encodePath(this.token, this.currentUrl)}?t=${this.counter}`; } @computed get encodedUrl () { return `http://${encodeUrl(this.token, this.currentUrl)}:${this._api.dappsPort}?t=${this.counter}`; } @computed get frameId () { return `_web_iframe_${this.counter}`; } @computed get isPristine () { return this.currentUrl === this.nextUrl; } @action gotoUrl = (_url) => { let url = (_url || this.nextUrl).trim().replace(/\/+$/, ''); if (!hasProtocol.test(url)) { url = `https://${url}`; } return this.generateToken(url).then(() => { transaction(() => { this.setNextUrl(url); this.setCurrentUrl(this.nextUrl); }); }); } @action reload = () => { transaction(() => { this.setLoading(true); this.counter = Date.now(); }); } @action restoreUrl = () => { this.setNextUrl(this.currentUrl); } @action setHistory = (urls) => { this.history = Object .keys(urls) .filter((url) => url && !url.startsWith(this._api.dappsUrl) && url.indexOf('127.0.0.1') === -1) .sort((urlA, urlB) => { const timeA = urls[urlA].getTime(); const timeB = urls[urlB].getTime(); if (timeA > timeB) { return -1; } else if (timeA < timeB) { return 1; } return 0; }) .map((url) => { const hostname = url.replace(/^http[s]?:\/\//, '').split('/')[0]; return { hostname, timestamp: urls[url], url }; }); } @action setLoading = (isLoading) => { this.isLoading = isLoading; } @action setToken = (token) => { this.token = token; } @action setCurrentUrl = (_url) => { const url = _url || this.currentUrl; transaction(() => { this.currentUrl = url; this.parsedUrl = parseUrl(url); this.saveLastUrl(); this.reload(); }); } @action setNextUrl = (url) => { this.nextUrl = url; } generateToken = (_url) => { this.setToken(null); return this._api.signer .generateWebProxyAccessToken(_url) .then((token) => { this.setToken(token); }) .catch((error) => { console.warn('generateToken', error); }); } loadHistory = () => { return this._api.parity .listRecentDapps() .then((apps) => { this.setHistory(apps); }) .catch((error) => { console.warn('loadHistory', error); }); } loadLastUrl = () => { return localStore.get(LS_LAST_ADDRESS) || DEFAULT_URL; } saveLastUrl = () => { return localStore.set(LS_LAST_ADDRESS, this.currentUrl); } static get (api) { if (!instance) { instance = new Store(api); } return instance; } } export { DEFAULT_URL, LS_LAST_ADDRESS };