From 7f3b1bd31e86a3ab6b84ee6775a2c2e4408d08ba Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 26 Jan 2017 17:37:52 +0100 Subject: [PATCH] HistoryStore for tracking relevant routes (#4305) * HistoryStore for tracking relevant routes * Default route is still /accounts * Fix copyright date-merge issue --- js/src/routes.js | 31 +++++++++++--- js/src/views/Dapps/builtin.json | 3 +- js/src/views/historyStore.js | 61 ++++++++++++++++++++++++++ js/src/views/historyStore.spec.js | 71 +++++++++++++++++++++++++++++++ js/src/views/index.js | 10 +++-- 5 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 js/src/views/historyStore.js create mode 100644 js/src/views/historyStore.spec.js diff --git a/js/src/routes.js b/js/src/routes.js index 326869e89..2de9f5ed3 100644 --- a/js/src/routes.js +++ b/js/src/routes.js @@ -16,11 +16,15 @@ import { Accounts, Account, Addresses, Address, Application, - Contract, Contracts, Dapp, Dapps, + Contract, Contracts, Dapp, Dapps, HistoryStore, Settings, SettingsBackground, SettingsParity, SettingsProxy, SettingsViews, Signer, Status, Wallet, Web, WriteContract } from '~/views'; +import builtinDapps from '~/views/Dapps/builtin.json'; + +const accountsHistory = HistoryStore.get('accounts'); +const dappsHistory = HistoryStore.get('dapps'); function handleDeprecatedRoute (nextState, replace) { const { address } = nextState.params; @@ -46,7 +50,13 @@ function redirectTo (path) { } const accountsRoutes = [ - { path: ':address', component: Account }, + { + path: ':address', + component: Account, + onEnter: ({ params }) => { + accountsHistory.add(params.address); + } + }, { path: '/wallet/:address', component: Wallet } ]; @@ -81,7 +91,7 @@ const routes = [ { path: '/settings', onEnter: redirectTo('/settings/views') } ]; -const appRoutes = [ +const childRoutes = [ { path: 'accounts', indexRoute: { component: Accounts }, @@ -107,9 +117,16 @@ const appRoutes = [ component: Settings, childRoutes: settingsRoutes }, - + { + path: 'app/:id', + component: Dapp, + onEnter: ({ params }) => { + if (!builtinDapps[params.id] || !builtinDapps[params.id].skipHistory) { + dappsHistory.add(params.id); + } + } + }, { path: 'apps', component: Dapps }, - { path: 'app/:id', component: Dapp }, { path: 'web', component: Web }, { path: 'web/:url', component: Web }, { path: 'signer', component: Signer } @@ -119,7 +136,7 @@ const appRoutes = [ if (process.env.NODE_ENV !== 'production') { const Playground = require('./playground').default; - appRoutes.push({ + childRoutes.push({ path: 'playground', component: Playground }); @@ -128,7 +145,7 @@ if (process.env.NODE_ENV !== 'production') { routes.push({ path: '/', component: Application, - childRoutes: appRoutes + childRoutes }); export default routes; diff --git a/js/src/views/Dapps/builtin.json b/js/src/views/Dapps/builtin.json index 30f1d1eb0..0415b602b 100644 --- a/js/src/views/Dapps/builtin.json +++ b/js/src/views/Dapps/builtin.json @@ -72,7 +72,8 @@ "author": "Parity Team ", "version": "1.0.0", "visible": true, - "skipBuild": true + "skipBuild": true, + "skipHistory": true }, { "id": "0xa635a9326814bded464190eddf0bdb90ce92d40ea2359cf553ea80e3c5a4076c", diff --git a/js/src/views/historyStore.js b/js/src/views/historyStore.js new file mode 100644 index 000000000..e28829b54 --- /dev/null +++ b/js/src/views/historyStore.js @@ -0,0 +1,61 @@ +// 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, observable } from 'mobx'; +import localStore from 'store'; + +const LS_KEY = '_parity::history'; +const MAX_ENTRIES = 5; + +const instances = {}; + +export default class Store { + @observable history = []; + + constructor (type) { + this.historyKey = `${LS_KEY}::${type}`; + this.load(); + } + + @action add = (entry) => { + this.history = [{ + timestamp: Date.now(), + entry + }].concat(this.history.filter((h) => h.entry !== entry)).slice(0, MAX_ENTRIES); + this.save(); + } + + @action clear = () => { + this.history = []; + this.save(); + } + + @action load = () => { + this.history = localStore.get(this.historyKey) || []; + } + + save = () => { + return localStore.set(this.historyKey, this.history); + } + + static get (type) { + if (!instances[type]) { + instances[type] = new Store(type); + } + + return instances[type]; + } +} diff --git a/js/src/views/historyStore.spec.js b/js/src/views/historyStore.spec.js new file mode 100644 index 000000000..446aea68b --- /dev/null +++ b/js/src/views/historyStore.spec.js @@ -0,0 +1,71 @@ +// 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 Store from './historyStore'; + +const TEST_ENTRY_1 = 'testing 1'; +const TEST_ENTRY_2 = 'testing 2'; +const TEST_ENTRY_3 = 'testing 3'; + +let store; + +function create () { + store = Store.get('test'); + store.clear(); + + return store; +} + +describe('views/HistoryStore', () => { + beforeEach(() => { + create(); + }); + + describe('@action', () => { + describe('add', () => { + it('adds the url to the list (front)', () => { + store.add(TEST_ENTRY_1); + expect(store.history[0].entry).to.equal(TEST_ENTRY_1); + }); + + it('adds multiples to the list', () => { + store.add(TEST_ENTRY_1); + store.add(TEST_ENTRY_2); + + expect(store.history.length).to.equal(2); + expect(store.history[0].entry).to.equal(TEST_ENTRY_2); + expect(store.history[1].entry).to.equal(TEST_ENTRY_1); + }); + + it('does not add duplicates', () => { + store.add(TEST_ENTRY_2); + store.add(TEST_ENTRY_2); + + expect(store.history.length).to.equal(1); + expect(store.history[0].entry).to.equal(TEST_ENTRY_2); + }); + }); + + describe('clear', () => { + it('empties the list', () => { + store.add(TEST_ENTRY_3); + store.clear(); + + expect(store.history.length).to.equal(0); + }); + }); + }); +}); diff --git a/js/src/views/index.js b/js/src/views/index.js index 11a69be86..b469d6310 100644 --- a/js/src/views/index.js +++ b/js/src/views/index.js @@ -21,15 +21,16 @@ import Addresses from './Addresses'; import Application from './Application'; import Contract from './Contract'; import Contracts from './Contracts'; -import WriteContract from './WriteContract'; import Dapp from './Dapp'; -import Web from './Web'; import Dapps from './Dapps'; +import HistoryStore from './historyStore'; import ParityBar from './ParityBar'; import Settings, { SettingsBackground, SettingsParity, SettingsProxy, SettingsViews } from './Settings'; import Signer from './Signer'; import Status from './Status'; import Wallet from './Wallet'; +import Web from './Web'; +import WriteContract from './WriteContract'; export { Account, @@ -39,9 +40,9 @@ export { Application, Contract, Contracts, - WriteContract, Dapp, Dapps, + HistoryStore, ParityBar, Settings, SettingsBackground, @@ -51,5 +52,6 @@ export { Signer, Status, Wallet, - Web + Web, + WriteContract };