}).find('Card');
+
+ expect(cards).to.have.length(2);
+ expect(cards.get(1).props.children.props.children).to.equal('testingHover');
+ });
+
it('renders the Title', () => {
const title = render({ title: 'title' }).find('Title');
diff --git a/js/src/ui/Icons/index.js b/js/src/ui/Icons/index.js
index 59cb31de6..62cb02105 100644
--- a/js/src/ui/Icons/index.js
+++ b/js/src/ui/Icons/index.js
@@ -14,66 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import AddIcon from 'material-ui/svg-icons/content/add';
-import AttachFileIcon from 'material-ui/svg-icons/editor/attach-file';
-import CancelIcon from 'material-ui/svg-icons/content/clear';
-import CheckIcon from 'material-ui/svg-icons/navigation/check';
-import CloseIcon from 'material-ui/svg-icons/navigation/close';
-import CompareIcon from 'material-ui/svg-icons/action/compare-arrows';
-import ComputerIcon from 'material-ui/svg-icons/hardware/desktop-mac';
-import ContractIcon from 'material-ui/svg-icons/action/code';
-import CopyIcon from 'material-ui/svg-icons/content/content-copy';
-import DashboardIcon from 'material-ui/svg-icons/action/dashboard';
-import DeleteIcon from 'material-ui/svg-icons/action/delete';
-import DoneIcon from 'material-ui/svg-icons/action/done-all';
-import EditIcon from 'material-ui/svg-icons/content/create';
-import FingerprintIcon from 'material-ui/svg-icons/action/fingerprint';
-import LinkIcon from 'material-ui/svg-icons/content/link';
-import LockedIcon from 'material-ui/svg-icons/action/lock';
-import MoveIcon from 'material-ui/svg-icons/action/open-with';
-import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward';
-import PrevIcon from 'material-ui/svg-icons/navigation/arrow-back';
-import PrintIcon from 'material-ui/svg-icons/action/print';
-import RefreshIcon from 'material-ui/svg-icons/action/autorenew';
-import SaveIcon from 'material-ui/svg-icons/content/save';
-import SendIcon from 'material-ui/svg-icons/content/send';
-import SnoozeIcon from 'material-ui/svg-icons/av/snooze';
-import StarCircleIcon from 'material-ui/svg-icons/action/stars';
-import StarIcon from 'material-ui/svg-icons/toggle/star';
-import StarOutlineIcon from 'material-ui/svg-icons/toggle/star-border';
-import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
-import VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
-import VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';
-
-export {
- AddIcon,
- AttachFileIcon,
- CancelIcon,
- CheckIcon,
- CloseIcon,
- CompareIcon,
- ComputerIcon,
- ContractIcon,
- CopyIcon,
- DashboardIcon,
- DeleteIcon,
- DoneIcon,
- EditIcon,
- FingerprintIcon,
- LinkIcon,
- LockedIcon,
- MoveIcon,
- NextIcon,
- PrevIcon,
- PrintIcon,
- RefreshIcon,
- SaveIcon,
- SendIcon,
- SnoozeIcon,
- StarIcon,
- StarCircleIcon,
- StarOutlineIcon,
- VerifyIcon,
- VisibleIcon,
- VpnIcon
-};
+export AddIcon from 'material-ui/svg-icons/content/add';
+export AttachFileIcon from 'material-ui/svg-icons/editor/attach-file';
+export CancelIcon from 'material-ui/svg-icons/content/clear';
+export CheckIcon from 'material-ui/svg-icons/navigation/check';
+export CloseIcon from 'material-ui/svg-icons/navigation/close';
+export CompareIcon from 'material-ui/svg-icons/action/compare-arrows';
+export ComputerIcon from 'material-ui/svg-icons/hardware/desktop-mac';
+export ContractIcon from 'material-ui/svg-icons/action/code';
+export CopyIcon from 'material-ui/svg-icons/content/content-copy';
+export DashboardIcon from 'material-ui/svg-icons/action/dashboard';
+export DeleteIcon from 'material-ui/svg-icons/action/delete';
+export DoneIcon from 'material-ui/svg-icons/action/done-all';
+export EditIcon from 'material-ui/svg-icons/content/create';
+export FingerprintIcon from 'material-ui/svg-icons/action/fingerprint';
+export LinkIcon from 'material-ui/svg-icons/content/link';
+export LockedIcon from 'material-ui/svg-icons/action/lock';
+export MoveIcon from 'material-ui/svg-icons/action/open-with';
+export NextIcon from 'material-ui/svg-icons/navigation/arrow-forward';
+export PrevIcon from 'material-ui/svg-icons/navigation/arrow-back';
+export PrintIcon from 'material-ui/svg-icons/action/print';
+export RefreshIcon from 'material-ui/svg-icons/action/autorenew';
+export SaveIcon from 'material-ui/svg-icons/content/save';
+export SendIcon from 'material-ui/svg-icons/content/send';
+export SnoozeIcon from 'material-ui/svg-icons/av/snooze';
+export StarCircleIcon from 'material-ui/svg-icons/action/stars';
+export StarIcon from 'material-ui/svg-icons/toggle/star';
+export StarOutlineIcon from 'material-ui/svg-icons/toggle/star-border';
+export VerifyIcon from 'material-ui/svg-icons/action/verified-user';
+export VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
+export VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';
diff --git a/js/src/ui/SectionList/sectionList.css b/js/src/ui/SectionList/sectionList.css
index 02340fd55..b338f981d 100644
--- a/js/src/ui/SectionList/sectionList.css
+++ b/js/src/ui/SectionList/sectionList.css
@@ -16,8 +16,8 @@
*/
.section {
- overflow-x: hidden;
position: relative;
+ width: 100%;
.overlay {
background: rgba(0, 0, 0, 0.85);
@@ -33,7 +33,6 @@
.row {
display: flex;
justify-content: center;
- overflow-x: hidden;
/* TODO: As per JS comments, the flex-base could be adjusted in the future to allow for */
/* case where <> 3 columns are required should the need arrise from a UI pov. */
@@ -42,38 +41,28 @@
cursor: pointer;
display: flex;
flex: 0 1 33.33%;
- opacity: 0.75;
- overflow-x: hidden;
+ max-width: 33.33%;
+ opacity: 0.85;
padding: 0.25em;
transition: all 0.75s cubic-bezier(0.23, 1, 0.32, 1);
- /* TODO: The hover and no-hover states can be improved to not "just appear" */
- &:not(:hover) {
- & [data-hover="hide"] {
- }
-
- & [data-hover="show"] {
- display: none;
- }
- }
-
&:hover {
opacity: 1;
z-index: 100;
-
- & [data-hover="hide"] {
- display: none;
- }
-
- & [data-hover="show"] {
- }
}
+ }
- &.stretch-on:hover {
- flex: 0 0 50%;
- }
+ &:hover {
+ .item {
+ &.stretchOn {
+ flex: 0 1 29%;
+ max-width: 29%;
- &.stretch-off:hover {
+ &:hover {
+ flex: 0 0 42%;
+ max-width: 42%;
+ }
+ }
}
}
}
diff --git a/js/src/ui/SectionList/sectionList.js b/js/src/ui/SectionList/sectionList.js
index 5e9106549..b326b9ed8 100644
--- a/js/src/ui/SectionList/sectionList.js
+++ b/js/src/ui/SectionList/sectionList.js
@@ -74,29 +74,34 @@ export default class SectionList extends Component {
className={ styles.row }
key={ `row_${index}` }
>
- { row.map(this.renderItem) }
+ {
+ row
+ .map(this.renderItem)
+ .filter((item) => item)
+ }
);
}
renderItem = (item, index) => {
const { noStretch, renderItem } = this.props;
+ const itemRendered = renderItem(item, index);
+
+ if (!itemRendered) {
+ return null;
+ }
- // NOTE: Any children that is to be showed or hidden (depending on hover state)
- // should have the data-hover="show|hide" attributes. For the current implementation
- // this does the trick, however there may be a case for adding a hover attribute
- // to an item (mouseEnter/mouseLeave events) and then adjusting the styling with
- // :root[hover]/:root:not[hover] for the tragetted elements. Currently it is a
- // CSS-only solution to let the browser do all the work via selectors.
return (
- );
+ { body }
+
+
+ );
+ });
}
}
diff --git a/js/src/views/Dapps/UrlButton/urlButton.js b/js/src/views/Dapps/UrlButton/urlButton.js
deleted file mode 100644
index 5f46225a9..000000000
--- a/js/src/views/Dapps/UrlButton/urlButton.js
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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 React, { Component, PropTypes } from 'react';
-import { FormattedMessage } from 'react-intl';
-import { withRouter } from 'react-router';
-
-import Button from '~/ui/Button';
-import { LinkIcon } from '~/ui/Icons';
-import Input from '~/ui/Form/Input';
-
-import styles from './urlButton.css';
-
-const INPUT_STYLE = { display: 'inline-block', width: '20em' };
-
-class UrlButton extends Component {
- static propTypes = {
- router: PropTypes.object.isRequired // injected by withRouter
- };
-
- state = {
- inputShown: false
- };
-
- render () {
- const { inputShown } = this.state;
-
- return (
-
- );
- }
-
- renderInput () {
- return (
-
- }
- onBlur={ this.hideInput }
- onFocus={ this.showInput }
- onSubmit={ this.inputOnSubmit }
- style={ INPUT_STYLE }
- />
- );
- }
-
- toggleInput = () => {
- const { inputShown } = this.state;
-
- this.setState({
- inputShown: !inputShown
- });
- }
-
- hideInput = () => {
- this.setState({ inputShown: false });
- }
-
- showInput = () => {
- this.setState({ inputShown: true });
- }
-
- inputOnSubmit = (url) => {
- const { router } = this.props;
-
- router.push(`/web/${encodeURIComponent(url)}`);
- }
-}
-
-export default withRouter(UrlButton);
diff --git a/js/src/views/Dapps/dapps.css b/js/src/views/Dapps/dapps.css
index d16317d60..6e3bdc14f 100644
--- a/js/src/views/Dapps/dapps.css
+++ b/js/src/views/Dapps/dapps.css
@@ -19,18 +19,20 @@
flex-wrap: wrap;
margin: -0.125em;
position: relative;
+
+ .item {
+ box-sizing: border-box;
+ flex: 0 1 50%;
+ opacity: 0.85;
+ padding: 0.125em;
+ }
+
}
.list+.list {
margin-top: -0.25em;
}
-.item {
- padding: 0.125em;
- flex: 0 1 50%;
- box-sizing: border-box;
-}
-
.overlay {
background: rgba(0, 0, 0, 0.85);
bottom: 0.5em;
diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js
index fc196fad8..cfdb2e716 100644
--- a/js/src/views/Dapps/dapps.js
+++ b/js/src/views/Dapps/dapps.js
@@ -26,7 +26,6 @@ import PermissionStore from '~/modals/DappPermissions/store';
import { Actionbar, Button, DappCard, Page } from '~/ui';
import { LockedIcon, VisibleIcon } from '~/ui/Icons';
-import UrlButton from './UrlButton';
import DappsStore from './dappsStore';
import styles from './dapps.css';
@@ -92,7 +91,6 @@ class Dapps extends Component {
/>
}
buttons={ [
- ,
}
key='edit'
diff --git a/js/src/views/Home/Accounts/accounts.css b/js/src/views/Home/Accounts/accounts.css
new file mode 100644
index 000000000..a6b08f344
--- /dev/null
+++ b/js/src/views/Home/Accounts/accounts.css
@@ -0,0 +1,50 @@
+/* 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 .
+*/
+
+.accounts {
+ margin-top: 1.5em;
+ text-align: center;
+
+ .account {
+ position: relative;
+ line-height: 2em;
+ vertical-align: middle;
+ white-space: nowrap;
+
+ .icon {
+ margin: 0;
+ }
+
+ .link, .name {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .name {
+ white-space: nowrap;
+ }
+
+ .timestamp {
+ color: #aaa;
+ font-size: 0.75em;
+ line-height: 1em;
+ padding-top: 0;
+ white-space: normal;
+ }
+ }
+}
diff --git a/js/src/views/Home/Accounts/accounts.js b/js/src/views/Home/Accounts/accounts.js
new file mode 100644
index 000000000..73ffce43b
--- /dev/null
+++ b/js/src/views/Home/Accounts/accounts.js
@@ -0,0 +1,139 @@
+// 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 moment from 'moment';
+import React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { connect } from 'react-redux';
+import { Link } from 'react-router';
+
+import { Container, ContainerTitle, IdentityName, IdentityIcon, SectionList } from '~/ui';
+import { arrayOrObjectProptype } from '~/util/proptypes';
+
+import styles from './accounts.css';
+
+class Accounts extends Component {
+ static propTypes = {
+ accountsInfo: PropTypes.object,
+ history: arrayOrObjectProptype().isRequired
+ };
+
+ render () {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ }
+
+ renderHistoryItem = (history) => {
+ const { accountsInfo } = this.props;
+
+ if (!history || !history.entry) {
+ return null;
+ }
+
+ const account = accountsInfo[history.entry] || { meta: {} };
+ let linkType = 'addresses';
+
+ if (account.uuid) {
+ linkType = 'accounts';
+ } else if (account.meta.wallet) {
+ linkType = 'wallet';
+ }
+
+ return (
+
+
+
+ }
+ >
+
+
+
+
+
+ );
+ }
+}
+
+function mapStateToProps (state) {
+ const { accountsInfo } = state.personal;
+
+ return {
+ accountsInfo
+ };
+}
+
+export default connect(
+ mapStateToProps,
+ null
+)(Accounts);
diff --git a/js/src/views/Home/Accounts/accounts.spec.js b/js/src/views/Home/Accounts/accounts.spec.js
new file mode 100644
index 000000000..368ebcedb
--- /dev/null
+++ b/js/src/views/Home/Accounts/accounts.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 { shallow } from 'enzyme';
+import React from 'react';
+import sinon from 'sinon';
+
+import Accounts from './';
+
+let component;
+let store;
+
+function createRedux () {
+ store = {
+ dispatch: sinon.stub(),
+ subscribe: sinon.stub(),
+ getState: () => {
+ return {
+ personal: {
+ accountsInfo: { '0x123': {} }
+ }
+ };
+ }
+ };
+
+ return store;
+}
+
+function render (history = []) {
+ component = shallow(
+ ,
+ {
+ context: {
+ store: createRedux()
+ }
+ }
+ ).find('Accounts').shallow();
+
+ return component;
+}
+
+describe('views/Home/Accounts', () => {
+ it('renders defaults', () => {
+ expect(render()).to.be.ok;
+ });
+
+ describe('no history', () => {
+ beforeEach(() => {
+ render();
+ });
+
+ it('renders empty message', () => {
+ expect(component.find('FormattedMessage').props().id).to.equal('home.accounts.none');
+ });
+ });
+});
diff --git a/js/src/views/Dapps/UrlButton/index.js b/js/src/views/Home/Accounts/index.js
similarity index 95%
rename from js/src/views/Dapps/UrlButton/index.js
rename to js/src/views/Home/Accounts/index.js
index 173beaadd..027387e70 100644
--- a/js/src/views/Dapps/UrlButton/index.js
+++ b/js/src/views/Home/Accounts/index.js
@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-export default from './urlButton';
+export default from './accounts';
diff --git a/js/src/views/Home/Dapps/dapp.js b/js/src/views/Home/Dapps/dapp.js
new file mode 100644
index 000000000..51e5c2122
--- /dev/null
+++ b/js/src/views/Home/Dapps/dapp.js
@@ -0,0 +1,89 @@
+// 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 moment from 'moment';
+import React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router';
+
+import { Container, DappIcon } from '~/ui';
+
+import styles from './dapps.css';
+
+export default class Dapp extends Component {
+ static propTypes = {
+ id: PropTypes.string.isRequired,
+ store: PropTypes.object.isRequired,
+ timestamp: PropTypes.number.isRequired
+ }
+
+ state = {
+ dapp: null
+ }
+
+ componentWillMount () {
+ return this.loadApp();
+ }
+
+ render () {
+ const { id, timestamp } = this.props;
+ const { dapp } = this.state;
+
+ if (!dapp) {
+ return null;
+ }
+
+ return (
+
+
+
+ }
+ >
+
+
+
+ { dapp.name }
+
+
+
+ );
+ }
+
+ loadApp = () => {
+ const { id, store } = this.props;
+
+ return store
+ .loadApp(id)
+ .then((dapp) => {
+ this.setState({ dapp });
+ });
+ }
+}
diff --git a/js/src/views/Home/Dapps/dapp.spec.js b/js/src/views/Home/Dapps/dapp.spec.js
new file mode 100644
index 000000000..d7c5b09bd
--- /dev/null
+++ b/js/src/views/Home/Dapps/dapp.spec.js
@@ -0,0 +1,55 @@
+// 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 { shallow } from 'enzyme';
+import React from 'react';
+
+import Dapp from './dapp';
+
+import { createStore } from './dapps.test.js';
+
+let component;
+let instance;
+let store;
+
+function render () {
+ store = createStore();
+ component = shallow(
+
+ );
+ instance = component.instance();
+
+ return component;
+}
+
+describe('views/Home/Dapp', () => {
+ beforeEach(() => {
+ render();
+ return instance.componentWillMount();
+ });
+
+ it('renders defaults', () => {
+ expect(component).to.be.ok;
+ });
+
+ it('loads the dapp on mount', () => {
+ expect(store.loadApp).to.have.been.calledWith('testId');
+ });
+});
diff --git a/js/src/views/Home/Dapps/dapps.css b/js/src/views/Home/Dapps/dapps.css
new file mode 100644
index 000000000..b8e9c01df
--- /dev/null
+++ b/js/src/views/Home/Dapps/dapps.css
@@ -0,0 +1,48 @@
+/* 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 .
+*/
+
+.dapps {
+ margin-top: 1.5em;
+ text-align: center;
+
+ .dapp {
+ position: relative;
+ line-height: 2em;
+ vertical-align: middle;
+
+ .icon {
+ margin: 0;
+ }
+
+ .link, .name {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .name {
+ white-space: nowrap;
+ }
+
+ .timestamp {
+ color: #aaa;
+ font-size: 0.75em;
+ line-height: 1em;
+ padding-top: 0;
+ }
+ }
+}
diff --git a/js/src/views/Home/Dapps/dapps.js b/js/src/views/Home/Dapps/dapps.js
new file mode 100644
index 000000000..608d4435b
--- /dev/null
+++ b/js/src/views/Home/Dapps/dapps.js
@@ -0,0 +1,86 @@
+// 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 React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
+
+import { ContainerTitle, SectionList } from '~/ui';
+import { arrayOrObjectProptype } from '~/util/proptypes';
+
+import Dapp from './dapp';
+import styles from './dapps.css';
+
+export default class Dapps extends Component {
+ static propTypes = {
+ history: arrayOrObjectProptype().isRequired,
+ store: PropTypes.object.isRequired
+ }
+
+ render () {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ }
+
+ renderHistoryItem = (history) => {
+ if (!history || !history.entry) {
+ return null;
+ }
+
+ const { store } = this.props;
+
+ return (
+
+ );
+ }
+}
diff --git a/js/src/views/Home/Dapps/dapps.spec.js b/js/src/views/Home/Dapps/dapps.spec.js
new file mode 100644
index 000000000..8dcb938f4
--- /dev/null
+++ b/js/src/views/Home/Dapps/dapps.spec.js
@@ -0,0 +1,68 @@
+// 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 { shallow } from 'enzyme';
+import React from 'react';
+
+import Dapps from './';
+
+import { createStore } from './dapps.test.js';
+
+let component;
+let store;
+
+function render (history = []) {
+ store = createStore();
+ component = shallow(
+
+ );
+
+ return component;
+}
+
+describe('views/Home/Dapps', () => {
+ it('renders defaults', () => {
+ expect(render()).to.be.ok;
+ });
+
+ describe('no history', () => {
+ beforeEach(() => {
+ render();
+ });
+
+ it('renders empty message', () => {
+ expect(component.find('FormattedMessage').props().id).to.equal('home.dapps.none');
+ });
+ });
+
+ describe('with history', () => {
+ const HISTORY = [
+ { timestamp: 1, entry: 'testABC' },
+ { timestamp: 2, entry: 'testDEF' }
+ ];
+
+ beforeEach(() => {
+ render(HISTORY);
+ });
+
+ it('renders SectionList', () => {
+ expect(component.find('SectionList').length).to.equal(1);
+ });
+ });
+});
diff --git a/js/src/views/Home/Dapps/dapps.test.js b/js/src/views/Home/Dapps/dapps.test.js
new file mode 100644
index 000000000..3593d7f3e
--- /dev/null
+++ b/js/src/views/Home/Dapps/dapps.test.js
@@ -0,0 +1,27 @@
+// 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 sinon from 'sinon';
+
+function createStore () {
+ return {
+ loadApp: sinon.stub().resolves({ name: 'testName' })
+ };
+}
+
+export {
+ createStore
+};
diff --git a/js/src/views/Home/Dapps/index.js b/js/src/views/Home/Dapps/index.js
new file mode 100644
index 000000000..9e6dddb63
--- /dev/null
+++ b/js/src/views/Home/Dapps/index.js
@@ -0,0 +1,17 @@
+// 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 .
+
+export default from './dapps';
diff --git a/js/src/views/Home/News/index.js b/js/src/views/Home/News/index.js
new file mode 100644
index 000000000..437e53a6e
--- /dev/null
+++ b/js/src/views/Home/News/index.js
@@ -0,0 +1,17 @@
+// 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 .
+
+export default from './news';
diff --git a/js/src/views/Home/News/news.css b/js/src/views/Home/News/news.css
new file mode 100644
index 000000000..f6a19affa
--- /dev/null
+++ b/js/src/views/Home/News/news.css
@@ -0,0 +1,73 @@
+/* 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 .
+*/
+
+.news {
+}
+
+.markdown {
+ line-height: 1.2em;
+}
+
+.item {
+ height: 240px;
+ opacity: 0.85;
+ position: relative;
+ width: 100%;
+
+ .background {
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+ background-size: cover;
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: auto;
+ }
+
+ .overlay {
+ background: white;
+ color: #333;
+ display: none;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 100%;
+ padding: 0 1.5em 1em 1.5em;
+ }
+
+ .title {
+ background: rgba(255, 255, 255, 0.85);
+ bottom: 0;
+ color: #333;
+ font-size: 1.17em;
+ left: 0;
+ padding: 1rem 1.5rem;
+ position: absolute;
+ right: 0;
+ text-transform: uppercase;
+ }
+
+ &:hover {
+ opacity: 1;
+
+ .overlay {
+ display: block;
+ }
+ }
+}
diff --git a/js/src/views/Home/News/news.js b/js/src/views/Home/News/news.js
new file mode 100644
index 000000000..8af395a6b
--- /dev/null
+++ b/js/src/views/Home/News/news.js
@@ -0,0 +1,92 @@
+// 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 { observer } from 'mobx-react';
+import React, { Component } from 'react';
+import ReactMarkdown from 'react-markdown';
+
+import { SectionList } from '~/ui';
+
+import { createRenderers } from './renderers';
+import Store from './store';
+import styles from './news.css';
+
+const VERSION_ID = '1';
+
+@observer
+export default class News extends Component {
+ store = Store.get();
+
+ componentWillMount () {
+ return this.store.retrieveNews(VERSION_ID);
+ }
+
+ render () {
+ const { newsItems } = this.store;
+
+ if (!newsItems || !newsItems.length) {
+ return null;
+ }
+
+ return (
+
+ );
+ }
+
+ renderItem = (item) => {
+ if (!item) {
+ return null;
+ }
+
+ const inlineStyles = item.style || {};
+
+ return (
+
+
+
+ { item.title }
+
+
+
+
+
+ );
+ }
+}
+
+export {
+ VERSION_ID
+};
diff --git a/js/src/views/Home/News/news.spec.js b/js/src/views/Home/News/news.spec.js
new file mode 100644
index 000000000..6da5c2919
--- /dev/null
+++ b/js/src/views/Home/News/news.spec.js
@@ -0,0 +1,54 @@
+// 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 { shallow } from 'enzyme';
+import React from 'react';
+
+import News from './news';
+import { restoreGlobals, stubGlobals } from './news.test.js';
+
+let component;
+let instance;
+
+function render () {
+ component = shallow(
+
+ );
+ instance = component.instance();
+
+ return component;
+}
+
+describe('views/Home/News', () => {
+ beforeEach(() => {
+ stubGlobals();
+ render();
+
+ return instance.componentWillMount();
+ });
+
+ afterEach(() => {
+ restoreGlobals();
+ });
+
+ it('renders defaults', () => {
+ expect(component).to.be.ok;
+ });
+
+ it('retrieves the content meta on mount', () => {
+ expect(instance.store.newsItems).to.equal('testContent');
+ });
+});
diff --git a/js/src/views/Home/News/news.test.js b/js/src/views/Home/News/news.test.js
new file mode 100644
index 000000000..4b45502a7
--- /dev/null
+++ b/js/src/views/Home/News/news.test.js
@@ -0,0 +1,54 @@
+// 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 sinon from 'sinon';
+
+import Contracts from '~/contracts';
+
+import { VERSION_ID } from './news';
+
+let contracts;
+let globalContractsGet;
+let globalFetch;
+
+export function stubGlobals () {
+ contracts = {
+ githubHint: {
+ getEntry: sinon.stub().resolves(['testUrl', 'testOwner', 'testCommit'])
+ },
+ registry: {
+ lookupMeta: sinon.stub().resolves('testMeta')
+ }
+ };
+
+ globalContractsGet = Contracts.get;
+ globalFetch = global.fetch;
+
+ sinon.stub(Contracts, 'get', () => contracts);
+ sinon.stub(global, 'fetch').resolves({
+ ok: true,
+ json: sinon.stub().resolves({
+ [VERSION_ID]: {
+ items: 'testContent'
+ }
+ })
+ });
+}
+
+export function restoreGlobals () {
+ Contracts.get = globalContractsGet;
+ global.fetch = globalFetch;
+}
diff --git a/js/src/views/Home/News/renderers.js b/js/src/views/Home/News/renderers.js
new file mode 100644
index 000000000..fde476029
--- /dev/null
+++ b/js/src/views/Home/News/renderers.js
@@ -0,0 +1,37 @@
+// 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 { createElement } from 'react';
+
+export function createRenderers (tagStyles = {}) {
+ return Object
+ .keys(tagStyles)
+ .reduce((renderers, tag) => {
+ switch (tag) {
+ case 'a':
+ case 'link':
+ renderers['link'] = (mdProps) => {
+ const { children, href, title } = mdProps;
+ const style = tagStyles[tag];
+
+ return createElement('a', { href, title, style }, children);
+ };
+ break;
+ }
+
+ return renderers;
+ }, {});
+}
diff --git a/js/src/views/Home/News/store.js b/js/src/views/Home/News/store.js
new file mode 100644
index 000000000..dd07d3b80
--- /dev/null
+++ b/js/src/views/Home/News/store.js
@@ -0,0 +1,67 @@
+// 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 Contracts from '~/contracts';
+
+let instance = null;
+
+export default class Store {
+ @observable newsItems = null;
+
+ @action setNewsItems = (newsItems) => {
+ this.newsItems = newsItems;
+ }
+
+ retrieveNews (versionId) {
+ const contracts = Contracts.get();
+
+ return contracts.registry
+ .lookupMeta('paritynews', 'CONTENT')
+ .then((contentId) => {
+ return contracts.githubHint.getEntry(contentId);
+ })
+ .then(([url, owner, commit]) => {
+ if (!url) {
+ return null;
+ }
+
+ return fetch(url).then((response) => {
+ if (!response.ok) {
+ return null;
+ }
+
+ return response.json();
+ });
+ })
+ .then((news) => {
+ if (news && news[versionId]) {
+ this.setNewsItems(news[versionId].items);
+ }
+ })
+ .catch((error) => {
+ console.warn('retrieveNews', error);
+ });
+ }
+
+ static get () {
+ if (!instance) {
+ instance = new Store();
+ }
+
+ return instance;
+ }
+}
diff --git a/js/src/views/Home/News/store.spec.js b/js/src/views/Home/News/store.spec.js
new file mode 100644
index 000000000..6e77e0ee8
--- /dev/null
+++ b/js/src/views/Home/News/store.spec.js
@@ -0,0 +1,57 @@
+// 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 { VERSION_ID } from './news';
+import { restoreGlobals, stubGlobals } from './news.test.js';
+import Store from './store';
+
+let store;
+
+function create () {
+ store = new Store();
+
+ return store;
+}
+
+describe('views/Home/News/Store', () => {
+ beforeEach(() => {
+ stubGlobals();
+ create();
+ });
+
+ afterEach(() => {
+ restoreGlobals();
+ });
+
+ describe('@action', () => {
+ describe('setNewsItems', () => {
+ it('sets the items', () => {
+ store.setNewsItems('testing');
+ expect(store.newsItems).to.equal('testing');
+ });
+ });
+ });
+
+ describe('operations', () => {
+ describe('retrieveNews', () => {
+ it('retrieves the items', () => {
+ return store.retrieveNews(VERSION_ID).then(() => {
+ expect(store.newsItems).to.equal('testContent');
+ });
+ });
+ });
+ });
+});
diff --git a/js/src/views/Home/Urls/index.js b/js/src/views/Home/Urls/index.js
new file mode 100644
index 000000000..d28700edb
--- /dev/null
+++ b/js/src/views/Home/Urls/index.js
@@ -0,0 +1,17 @@
+// 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 .
+
+export default from './urls';
diff --git a/js/src/views/Home/Urls/urls.css b/js/src/views/Home/Urls/urls.css
new file mode 100644
index 000000000..5b5deeb5b
--- /dev/null
+++ b/js/src/views/Home/Urls/urls.css
@@ -0,0 +1,80 @@
+/* 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 .
+*/
+
+.urls {
+ text-align: center;
+
+ .layout {
+ box-sizing: border-box;
+ margin: 1.5em auto;
+ padding: 0 1em;
+ width: 50%;
+
+ .empty {
+ margin-top: 0.5em;
+ opacity: 0.75;
+ }
+
+ .historyItem {
+ height: auto;
+ margin-top: 1em;
+ position: relative;
+ width: 100%;
+
+ .linkIcon {
+ opacity: 0;
+ position: absolute;
+ right: 0.5em;
+ top: 0.5em;
+ }
+
+ .url {
+ display: block;
+ color: rgb(0, 151, 167);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .timestamp {
+ color: #aaa;
+ font-size: 0.75em;
+ line-height: 1em;
+ padding-top: 0.5rem;
+ }
+
+ &:hover {
+ opacity: 1;
+
+ .linkIcon {
+ opacity: 1;
+ }
+ }
+ }
+
+ .input {
+ background: rgba(255, 255, 255, 0.25);
+ border: 1px solid rgba(255, 255, 255, 0.5);
+ border-radius: 0.25em;
+ box-sizing: border-box;
+ color: white;
+ display: block;
+ font-size: 1.25em;
+ padding: 0.5em;
+ width: 100%;
+ }
+ }
+}
diff --git a/js/src/views/Home/Urls/urls.js b/js/src/views/Home/Urls/urls.js
new file mode 100644
index 000000000..7ded1ecda
--- /dev/null
+++ b/js/src/views/Home/Urls/urls.js
@@ -0,0 +1,139 @@
+// 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 { observer } from 'mobx-react';
+import moment from 'moment';
+import React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
+
+import { Container, ContainerTitle, DappUrlInput, SectionList } from '~/ui';
+import { LinkIcon } from '~/ui/Icons';
+
+import styles from './urls.css';
+
+@observer
+export default class Urls extends Component {
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
+
+ static propTypes = {
+ extensionStore: PropTypes.object.isRequired,
+ store: PropTypes.object.isRequired
+ }
+
+ render () {
+ const { nextUrl } = this.props.store;
+
+ return (
+
+
+ );
+ }
+
+ onChangeUrl = (url) => {
+ this.props.store.setNextUrl(url);
+ }
+
+ onGotoUrl = (url) => {
+ const { router } = this.context;
+ const { extensionStore } = this.props;
+
+ this.props.store.gotoUrl(url);
+
+ if (extensionStore.hasExtension) {
+ window.open(this.props.store.currentUrl, '_blank');
+ } else {
+ router.push('/web');
+ }
+ }
+
+ onRestoreUrl = () => {
+ this.props.store.restoreUrl();
+ }
+}
diff --git a/js/src/views/Home/Urls/urls.spec.js b/js/src/views/Home/Urls/urls.spec.js
new file mode 100644
index 000000000..6c1438fe0
--- /dev/null
+++ b/js/src/views/Home/Urls/urls.spec.js
@@ -0,0 +1,124 @@
+// 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 { shallow } from 'enzyme';
+import React from 'react';
+import sinon from 'sinon';
+
+import Urls from './';
+
+const NEXT_URL = 'http://somewhere.next';
+
+let component;
+let instance;
+let router;
+let store;
+
+function createRouter () {
+ router = {
+ push: sinon.stub()
+ };
+
+ return router;
+}
+
+function createStore () {
+ store = {
+ history: [],
+ gotoUrl: sinon.stub(),
+ restoreUrl: sinon.stub(),
+ setNextUrl: sinon.stub(),
+ nextUrl: NEXT_URL
+ };
+
+ return store;
+}
+
+function render () {
+ component = shallow(
+ ,
+ {
+ context: {
+ router: createRouter()
+ }
+ }
+ );
+ instance = component.instance();
+
+ return component;
+}
+
+describe('views/Home/Urls', () => {
+ beforeEach(() => {
+ render();
+ });
+
+ it('renders defaults', () => {
+ expect(component).to.be.ok;
+ });
+
+ describe('input', () => {
+ let input;
+
+ beforeEach(() => {
+ input = component.find('DappUrlInput');
+ });
+
+ it('renders the input cmponent', () => {
+ expect(input.length).to.equal(1);
+ });
+
+ it('passes nextUrl as url', () => {
+ expect(input.props().url).to.equal(NEXT_URL);
+ });
+ });
+
+ describe('events', () => {
+ describe('onChangeUrl', () => {
+ it('performs setNextUrl on store', () => {
+ instance.onChangeUrl('123');
+ expect(store.setNextUrl).to.have.been.calledWith('123');
+ });
+ });
+
+ describe('onGotoUrl', () => {
+ it('performs gotoUrl on store', () => {
+ instance.onGotoUrl();
+ expect(store.gotoUrl).to.have.been.called;
+ });
+
+ it('passed the URL when provided', () => {
+ instance.onGotoUrl('http://example.com');
+ expect(store.gotoUrl).to.have.been.calledWith('http://example.com');
+ });
+
+ it('does route navigation when executed', () => {
+ instance.onGotoUrl();
+ expect(router.push).to.have.been.calledWith('/web');
+ });
+ });
+
+ describe('onRestoreUrl', () => {
+ it('performs restoreUrl on store', () => {
+ instance.onRestoreUrl();
+ expect(store.restoreUrl).to.have.been.called;
+ });
+ });
+ });
+});
diff --git a/js/src/views/Dapps/UrlButton/urlButton.css b/js/src/views/Home/home.css
similarity index 74%
rename from js/src/views/Dapps/UrlButton/urlButton.css
rename to js/src/views/Home/home.css
index ce2f78693..c607e4993 100644
--- a/js/src/views/Dapps/UrlButton/urlButton.css
+++ b/js/src/views/Home/home.css
@@ -15,6 +15,26 @@
/* along with Parity. If not, see .
*/
-.button {
- vertical-align: middle;
+.accounts {
+ margin-top: 1.5em;
+}
+
+.body {
+ padding-bottom: 3em;
+}
+
+.empty {
+ margin-top: 1.5em;
+ opacity: 0.5;
+}
+
+.row {
+ display: flex;
+
+ .column {
+ box-sizing: border-box;
+ flex: 0 1 50%;
+ padding: 0 1.5em;
+ width: 50%;
+ }
}
diff --git a/js/src/views/Home/home.js b/js/src/views/Home/home.js
new file mode 100644
index 000000000..e1ebd37a5
--- /dev/null
+++ b/js/src/views/Home/home.js
@@ -0,0 +1,81 @@
+// 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 { observer } from 'mobx-react';
+import React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
+
+import { Page } from '~/ui';
+
+import DappsStore from '../Dapps/dappsStore';
+import ExtensionStore from '../Application/Extension/store';
+import HistoryStore from '../historyStore';
+import WebStore from '../Web/store';
+
+import Accounts from './Accounts';
+import Dapps from './Dapps';
+import News from './News';
+import Urls from './Urls';
+import styles from './home.css';
+
+@observer
+export default class Home extends Component {
+ static contextTypes = {
+ api: PropTypes.object.isRequired
+ };
+
+ dappsStore = DappsStore.get(this.context.api);
+ extensionStore = ExtensionStore.get();
+ webStore = WebStore.get(this.context.api);
+
+ accountsHistory = HistoryStore.get('accounts');
+ dappsHistory = HistoryStore.get('dapps');
+
+ componentWillMount () {
+ return this.webStore.loadHistory();
+ }
+
+ render () {
+ return (
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/js/src/views/Home/home.spec.js b/js/src/views/Home/home.spec.js
new file mode 100644
index 000000000..2fccccee1
--- /dev/null
+++ b/js/src/views/Home/home.spec.js
@@ -0,0 +1,96 @@
+// 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 { shallow } from 'enzyme';
+import React from 'react';
+import sinon from 'sinon';
+
+import Home from './';
+
+const TEST_APP_HISTORY = [];
+
+let api;
+let component;
+let instance;
+
+function createApi () {
+ api = {
+ parity: {
+ listRecentDapps: sinon.stub().resolves(TEST_APP_HISTORY)
+ }
+ };
+
+ return api;
+}
+
+function render () {
+ component = shallow(
+ ,
+ {
+ context: {
+ api: createApi()
+ }
+ }
+ );
+ instance = component.instance();
+
+ return component;
+}
+
+describe('views/Home', () => {
+ beforeEach(() => {
+ render();
+ });
+
+ it('renders defaults', () => {
+ expect(component).to.be.ok;
+ });
+
+ describe('lifecycle', () => {
+ describe('componentWillMount', () => {
+ beforeEach(() => {
+ sinon.stub(instance.webStore, 'loadHistory');
+ return instance.componentWillMount();
+ });
+
+ afterEach(() => {
+ instance.webStore.loadHistory.restore();
+ });
+
+ it('calls into webStore loadHistory', () => {
+ expect(instance.webStore.loadHistory).to.have.been.called;
+ });
+ });
+ });
+
+ describe('components', () => {
+ it('renders Accounts', () => {
+ expect(component.find('Connect(Accounts)').length).to.equal(1);
+ });
+
+ it('renders Dapps', () => {
+ expect(component.find('Dapps').length).to.equal(1);
+ });
+
+ it('renders News', () => {
+ expect(component.find('News').length).to.equal(1);
+ });
+
+ it('renders Urls', () => {
+ expect(component.find('Urls').length).to.equal(1);
+ });
+ });
+});
diff --git a/js/src/views/Home/index.js b/js/src/views/Home/index.js
new file mode 100644
index 000000000..12e31704a
--- /dev/null
+++ b/js/src/views/Home/index.js
@@ -0,0 +1,17 @@
+// 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 .
+
+export default from './home';
diff --git a/js/src/views/ParityBar/parityBar.css b/js/src/views/ParityBar/parityBar.css
index 6693f5727..64b0b3521 100644
--- a/js/src/views/ParityBar/parityBar.css
+++ b/js/src/views/ParityBar/parityBar.css
@@ -19,20 +19,7 @@ $overlayZ: 10000;
$modalZ: 10001;
.account {
- display: flex;
- flex: 1;
- overflow: hidden;
- position: relative;
-
- .accountOverlay {
- position: absolute;
- right: 0.5em;
- top: 0.5em;
- }
-
- .iconDisabled {
- opacity: 0.15;
- }
+ width: 100%;
.selected,
.unselected {
diff --git a/js/src/views/Settings/Views/defaults.js b/js/src/views/Settings/Views/defaults.js
index 5de5dc5f5..ef2bc910d 100644
--- a/js/src/views/Settings/Views/defaults.js
+++ b/js/src/views/Settings/Views/defaults.js
@@ -23,7 +23,24 @@ import CommunicationContacts from 'material-ui/svg-icons/communication/contacts'
import ImageGridOn from 'material-ui/svg-icons/image/grid-on';
import NavigationApps from 'material-ui/svg-icons/navigation/apps';
+import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
+
+import styles from './views.css';
+
const defaultViews = {
+ home: {
+ active: true,
+ fixed: true,
+ icon: (
+
+ ),
+ route: '/home',
+ value: 'home'
+ },
+
accounts: {
active: true,
fixed: true,
diff --git a/js/src/views/Settings/Views/views.css b/js/src/views/Settings/Views/views.css
index a04d04783..de4079608 100644
--- a/js/src/views/Settings/Views/views.css
+++ b/js/src/views/Settings/Views/views.css
@@ -43,6 +43,12 @@
color: white !important;
}
+.logoIcon {
+ height: 24px;
+ margin-bottom: 5px;
+ opacity: 0.5;
+}
+
.info {
color: #aaa;
padding-left: 4.75em;
diff --git a/js/src/views/Web/store.js b/js/src/views/Web/store.js
index 542b47a7b..e62effb89 100644
--- a/js/src/views/Web/store.js
+++ b/js/src/views/Web/store.js
@@ -82,8 +82,31 @@ export default class Store {
this.setNextUrl(this.currentUrl);
}
- @action setHistory = (history) => {
- this.history = history;
+ @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) => {
diff --git a/js/src/views/Web/store.spec.js b/js/src/views/Web/store.spec.js
index 8a8dd268c..58b2f1b3c 100644
--- a/js/src/views/Web/store.spec.js
+++ b/js/src/views/Web/store.spec.js
@@ -18,7 +18,13 @@ import sinon from 'sinon';
import Store from './store';
-const TEST_HISTORY = ['somethingA', 'somethingB'];
+const TEST_HISTORY_URLA = 'http://testingA';
+const TEST_HISTORY_URLB = 'http://testingB';
+const TEST_HISTORY = {
+ '': new Date(678),
+ [TEST_HISTORY_URLA]: new Date(123),
+ [TEST_HISTORY_URLB]: new Date(456)
+};
const TEST_TOKEN = 'testing-123';
const TEST_URL1 = 'http://some.test.domain.com';
const TEST_URL2 = 'http://something.different.com';
@@ -89,9 +95,28 @@ describe('views/Web/Store', () => {
});
describe('setHistory', () => {
- it('sets the history', () => {
+ let history;
+
+ beforeEach(() => {
store.setHistory(TEST_HISTORY);
- expect(store.history.peek()).to.deep.equal(TEST_HISTORY);
+ history = store.history.peek();
+ });
+
+ it('sets the history', () => {
+ expect(history.length).to.equal(2);
+ });
+
+ it('adds hostname to entries', () => {
+ expect(history[1].hostname).to.be.ok;
+ });
+
+ it('removes hostname http prefixes', () => {
+ expect(history[1].hostname.indexOf('http')).to.equal(-1);
+ });
+
+ it('sorts the entries according to recently accessed', () => {
+ expect(history[0].url).to.equal(TEST_HISTORY_URLB);
+ expect(history[1].url).to.equal(TEST_HISTORY_URLA);
});
});
@@ -195,7 +220,7 @@ describe('views/Web/Store', () => {
});
it('sets the history as retrieved', () => {
- expect(store.history.peek()).to.deep.equal(TEST_HISTORY);
+ expect(store.history.peek().length).not.to.equal(0);
});
});
});
diff --git a/js/src/views/historyStore.js b/js/src/views/historyStore.js
index e28829b54..fd18fd931 100644
--- a/js/src/views/historyStore.js
+++ b/js/src/views/historyStore.js
@@ -30,10 +30,11 @@ export default class Store {
this.load();
}
- @action add = (entry) => {
+ @action add = (entry, type) => {
this.history = [{
+ entry,
timestamp: Date.now(),
- entry
+ type
}].concat(this.history.filter((h) => h.entry !== entry)).slice(0, MAX_ENTRIES);
this.save();
}
diff --git a/js/src/views/index.js b/js/src/views/index.js
index 69b5d25f9..4701a7356 100644
--- a/js/src/views/index.js
+++ b/js/src/views/index.js
@@ -24,6 +24,7 @@ export Contracts from './Contracts';
export Dapp from './Dapp';
export Dapps from './Dapps';
export HistoryStore from './historyStore';
+export Home from './Home';
export ParityBar from './ParityBar';
export Settings, { SettingsBackground, SettingsParity, SettingsProxy, SettingsViews } from './Settings';
export Signer from './Signer';