New Homepage (#7266)

* Purify dappCard style

* Add support for pinning apps

* Add a section to show pinned apps

* Cleaner code

* Bump dependency versions

* Small tweaks

* Avoid double scrollbars

* Small style updates

* Bump parity/shared version
This commit is contained in:
Amaury Martiny 2017-12-12 17:35:21 +01:00 committed by Jaco Greeff
parent c731b5ef62
commit 1851453f00
11 changed files with 201 additions and 168 deletions

32
js/package-lock.json generated
View File

@ -15,9 +15,9 @@
} }
}, },
"@parity/api": { "@parity/api": {
"version": "2.1.5", "version": "2.1.6",
"resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.6.tgz",
"integrity": "sha512-HkvMIhIwDMEIyTmXqEjWn1C2qes0qJO270bQldRfCZf0XiOGXG726EzV3FUpUbVONCVQ9riDviAl3fw6D+N6nA==", "integrity": "sha512-4HuUJLGkZEAHSy5918ofepKXWvAE89VsoUN7q1Px9ASN8xLR3MWPMuNxOJZjKTqVDZWpXs8q3g7GIOVdi90BXA==",
"requires": { "requires": {
"@parity/abi": "2.1.2", "@parity/abi": "2.1.2",
"@parity/jsonrpc": "2.1.4", "@parity/jsonrpc": "2.1.4",
@ -52,8 +52,8 @@
"version": "github:js-dist-paritytech/dapp-dapp-methods#7245089a8e83274372cde3c9406ae155e2083f84", "version": "github:js-dist-paritytech/dapp-dapp-methods#7245089a8e83274372cde3c9406ae155e2083f84",
"dev": true, "dev": true,
"requires": { "requires": {
"@parity/api": "2.1.5", "@parity/api": "2.1.6",
"@parity/shared": "2.2.12", "@parity/shared": "2.2.14",
"@parity/ui": "2.2.15", "@parity/ui": "2.2.15",
"lodash": "4.17.4", "lodash": "4.17.4",
"mobx": "3.3.2", "mobx": "3.3.2",
@ -74,9 +74,9 @@
"integrity": "sha512-6bICFA1c1GBz4d7vratkoqovBezJNjc8VCwnZtpPTcyLeMshAhatPV4dGgJo/eHtlOCkKAeaAKatWZhEtXt/5g==", "integrity": "sha512-6bICFA1c1GBz4d7vratkoqovBezJNjc8VCwnZtpPTcyLeMshAhatPV4dGgJo/eHtlOCkKAeaAKatWZhEtXt/5g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@parity/api": "2.1.5", "@parity/api": "2.1.6",
"@parity/etherscan": "2.1.3", "@parity/etherscan": "2.1.3",
"@parity/shared": "2.2.12", "@parity/shared": "2.2.14",
"bignumber.js": "3.0.1", "bignumber.js": "3.0.1",
"brace": "0.9.0", "brace": "0.9.0",
"date-difference": "1.0.0", "date-difference": "1.0.0",
@ -556,7 +556,7 @@
"version": "github:js-dist-paritytech/dapp-dapp-visible#84f40feb42f1707d7f463213a990600dc11978f7", "version": "github:js-dist-paritytech/dapp-dapp-visible#84f40feb42f1707d7f463213a990600dc11978f7",
"dev": true, "dev": true,
"requires": { "requires": {
"@parity/api": "2.1.5", "@parity/api": "2.1.6",
"@parity/ui": "2.2.15", "@parity/ui": "2.2.15",
"mobx": "3.3.2", "mobx": "3.3.2",
"mobx-react": "4.3.5", "mobx-react": "4.3.5",
@ -576,9 +576,9 @@
"integrity": "sha512-6bICFA1c1GBz4d7vratkoqovBezJNjc8VCwnZtpPTcyLeMshAhatPV4dGgJo/eHtlOCkKAeaAKatWZhEtXt/5g==", "integrity": "sha512-6bICFA1c1GBz4d7vratkoqovBezJNjc8VCwnZtpPTcyLeMshAhatPV4dGgJo/eHtlOCkKAeaAKatWZhEtXt/5g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@parity/api": "2.1.5", "@parity/api": "2.1.6",
"@parity/etherscan": "2.1.3", "@parity/etherscan": "2.1.3",
"@parity/shared": "2.2.12", "@parity/shared": "2.2.14",
"bignumber.js": "3.0.1", "bignumber.js": "3.0.1",
"brace": "0.9.0", "brace": "0.9.0",
"date-difference": "1.0.0", "date-difference": "1.0.0",
@ -1103,7 +1103,7 @@
"resolved": "https://registry.npmjs.org/@parity/etherscan/-/etherscan-2.1.3.tgz", "resolved": "https://registry.npmjs.org/@parity/etherscan/-/etherscan-2.1.3.tgz",
"integrity": "sha512-GtQMaE8t7PDOcz/K4Ud+Z6EELB47+qG5V6R7iTJ4DcueXVgiMAXK5OiNeKF3Qjd1/M4FIJdFm5NTSdC7bR38+Q==", "integrity": "sha512-GtQMaE8t7PDOcz/K4Ud+Z6EELB47+qG5V6R7iTJ4DcueXVgiMAXK5OiNeKF3Qjd1/M4FIJdFm5NTSdC7bR38+Q==",
"requires": { "requires": {
"@parity/api": "2.1.5", "@parity/api": "2.1.6",
"bignumber.js": "3.0.1", "bignumber.js": "3.0.1",
"es6-promise": "4.1.1", "es6-promise": "4.1.1",
"node-fetch": "1.7.3", "node-fetch": "1.7.3",
@ -1140,9 +1140,9 @@
"version": "github:paritytech/plugin-signer-qr#c16423de5b8a8f68ebd5f1e78e084fa959329a9f" "version": "github:paritytech/plugin-signer-qr#c16423de5b8a8f68ebd5f1e78e084fa959329a9f"
}, },
"@parity/shared": { "@parity/shared": {
"version": "2.2.12", "version": "2.2.14",
"resolved": "https://registry.npmjs.org/@parity/shared/-/shared-2.2.12.tgz", "resolved": "https://registry.npmjs.org/@parity/shared/-/shared-2.2.14.tgz",
"integrity": "sha512-2ANbEOkWoqOf5ytE0K5pq7ZeqS7PVuiIwrhyxDgn8dQhpFiDLHro7pQirIEDZ8LzZK6g6V+HU38sagn6dYSRIQ==", "integrity": "sha512-YM7ZqWNyquX0+j89NPPxFBCUPgaqIVrqQ4Iqml50kz9uC/Ev07A/OB/+yYmAJ+3io6LWvDc/U5K3CpaMltTLtg==",
"requires": { "requires": {
"@parity/ledger": "2.1.2", "@parity/ledger": "2.1.2",
"eventemitter3": "2.0.3", "eventemitter3": "2.0.3",
@ -1202,9 +1202,9 @@
"resolved": "https://registry.npmjs.org/@parity/ui/-/ui-3.0.16.tgz", "resolved": "https://registry.npmjs.org/@parity/ui/-/ui-3.0.16.tgz",
"integrity": "sha512-yGQ8k2/oNxu0GyJ6eQS1AUk1O1XR//oXfaHPEBa0VuJIB7gY9lPrkG7CSNpazOrTCfhdCHgpjGe3WR5lbv0HiQ==", "integrity": "sha512-yGQ8k2/oNxu0GyJ6eQS1AUk1O1XR//oXfaHPEBa0VuJIB7gY9lPrkG7CSNpazOrTCfhdCHgpjGe3WR5lbv0HiQ==",
"requires": { "requires": {
"@parity/api": "2.1.5", "@parity/api": "2.1.6",
"@parity/etherscan": "2.1.3", "@parity/etherscan": "2.1.3",
"@parity/shared": "2.2.12", "@parity/shared": "2.2.14",
"babel-runtime": "6.26.0", "babel-runtime": "6.26.0",
"bignumber.js": "4.1.0", "bignumber.js": "4.1.0",
"brace": "0.11.0", "brace": "0.11.0",

View File

@ -140,12 +140,12 @@
"yargs": "6.6.0" "yargs": "6.6.0"
}, },
"dependencies": { "dependencies": {
"@parity/api": "^2.1.1", "@parity/api": "^2.1.6",
"@parity/plugin-signer-account": "paritytech/plugin-signer-account", "@parity/plugin-signer-account": "paritytech/plugin-signer-account",
"@parity/plugin-signer-default": "paritytech/plugin-signer-default", "@parity/plugin-signer-default": "paritytech/plugin-signer-default",
"@parity/plugin-signer-hardware": "paritytech/plugin-signer-hardware", "@parity/plugin-signer-hardware": "paritytech/plugin-signer-hardware",
"@parity/plugin-signer-qr": "paritytech/plugin-signer-qr", "@parity/plugin-signer-qr": "paritytech/plugin-signer-qr",
"@parity/shared": "^2.2.12", "@parity/shared": "2.2.14",
"@parity/ui": "^3.0.16", "@parity/ui": "^3.0.16",
"keythereum": "1.0.2", "keythereum": "1.0.2",
"lodash.flatten": "4.4.0", "lodash.flatten": "4.4.0",

View File

@ -20,37 +20,28 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
overflow: hidden;
width: 100%; width: 100%;
.logo { .logo {
top: 0;
right: 0;
left: 0;
bottom: 0;
opacity: 0.2;
position: absolute; position: absolute;
padding: 2em; left: 50%;
text-align: center; top: 50%;
transform: translate(-50%, -50%);
z-index: 0; z-index: 0;
opacity: 0.2;
img {
display: inline-block;
margin: 0 auto;
}
} }
.content { .content {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
overflow: hidden;
position: relative; position: relative;
> div:not(.logo) { /* Show scrollbar for Homepage only, dapps' scrollbar are handled inside
display: flex; * their iframe.
flex-grow: 1; */
overflow-y: scroll; & > div {
overflow-y: auto;
} }
} }

View File

@ -75,6 +75,7 @@ class Application extends Component {
return ( return (
<div className={ styles.application }> <div className={ styles.application }>
<img src={ parityLogo } className={ styles.logo } />
{ {
blockNumber blockNumber
? <Status upgradeStore={ this.upgradeStore } /> ? <Status upgradeStore={ this.upgradeStore } />
@ -107,33 +108,32 @@ class Application extends Component {
} }
renderApp () { renderApp () {
const { children } = this.props;
return [ return [
<Extension key='extension' />, <Extension key='extension' />,
<FirstRun key='firstrun' />, <FirstRun key='firstrun' />,
<Snackbar key='snackbar' />, <Snackbar key='snackbar' />,
<UpgradeParity key='upgrade' upgradeStore={ this.upgradeStore } />, <UpgradeParity key='upgrade' upgradeStore={ this.upgradeStore } />,
<Errors key='errors' />, <Errors key='errors' />,
<div key='content' className={ styles.content }> this.renderContent()
{ children }
</div>
]; ];
} }
renderMinimized () { renderMinimized () {
const { children } = this.props;
return [ return [
<Errors key='errors' />, <Errors key='errors' />,
<div key='content' className={ styles.content }> this.renderContent()
<div className={ styles.logo }>
<img src={ parityLogo } />
</div>
{ children }
</div>
]; ];
} }
renderContent () {
const { children } = this.props;
return (
<div key='content' className={ styles.content }>
{children}
</div>
);
}
} }
function mapStateToProps (state) { function mapStateToProps (state) {

View File

@ -72,7 +72,7 @@
.icons { .icons {
margin-top: 2em; margin-top: 2em;
margin-bottom: 0px; margin-bottom: 0;
} }
.icon, .icon,

View File

@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import GradientBg from '@parity/ui/lib/GradientBg'; import GradientBg from '@parity/ui/lib/GradientBg';
import Input from '@parity/ui/lib/Form/Input'; import Input from '@parity/ui/lib/Form/Input';
import { CompareIcon, ComputerIcon, DashboardIcon, VpnIcon, KeyIcon } from '@parity/ui/lib/Icons'; import { CompareIcon, ComputerIcon, DashboardIcon, KeyIcon } from '@parity/ui/lib/Icons';
import styles from './connection.css'; import styles from './connection.css';

View File

@ -16,48 +16,44 @@
*/ */
.card { .card {
padding: 0.25em; position: relative;
margin-top: 3em;
.pin {
display: none;
position: absolute;
top: 0;
right: 1.6em;
z-index: 1;
}
.pin.pinned {
display: block;
transform: rotate(45deg);
}
&:hover {
.pin {
display: block;
}
}
.content { .content {
background: white; display: block;
box-sizing: content-box; padding-top: 1em;
min-height: 100px;
.title { .title {
min-height: 1em; margin-top: 0.8em;
padding-left: 72px;
}
.description {
color: #333;
min-height: 1em;
padding-left: 72px;
padding-top: 0.25em;
opacity: 0.66;
} }
.image { .image {
border-radius: 8px; box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.15);
position: absolute; border-radius: 10px;
} display: block;
width: 2em;
.vouching { height: 2em;
padding: 0.5em; margin-left: auto;
margin: 0.5em 1em; margin-right: auto;
background: transparent;
position: relative;
white-space: nowrap;
img {
position: relative;
display: inline-block;
margin: 0.25em 0.25em 0.25em 0;
}
div {
right: auto;
left: 0;
}
} }
} }
} }

View File

@ -16,10 +16,11 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Link } from 'react-router';
import Container from '@parity/ui/lib/Container';
import DappIcon from '@parity/ui/lib/DappIcon'; import DappIcon from '@parity/ui/lib/DappIcon';
import DappVouchFor from '@parity/ui/lib/DappVouchFor'; import Header from 'semantic-ui-react/dist/commonjs/elements/Header';
import Button from 'semantic-ui-react/dist/commonjs/elements/Button';
import styles from './dappCard.css'; import styles from './dappCard.css';
@ -27,11 +28,15 @@ export default class DappCard extends Component {
static propTypes = { static propTypes = {
app: PropTypes.object.isRequired, app: PropTypes.object.isRequired,
availability: PropTypes.string.isRequired, availability: PropTypes.string.isRequired,
className: PropTypes.string className: PropTypes.string,
onPin: PropTypes.func,
pinned: PropTypes.bool
}; };
handlePin = () => this.props.onPin(this.props.app.id)
render () { render () {
const { app, availability, className } = this.props; const { app, availability, className, pinned } = this.props;
if (app.onlyPersonal && availability !== 'personal') { if (app.onlyPersonal && availability !== 'personal') {
return null; return null;
@ -39,26 +44,28 @@ export default class DappCard extends Component {
return ( return (
<div className={ [styles.card, className].join(' ') }> <div className={ [styles.card, className].join(' ') }>
<Container <Button
className={ styles.content } size='mini'
link={ `/${app.id}` } icon='pin'
> circular
<DappIcon className={ [styles.pin, pinned && styles.pinned].join(' ') }
app={ app } onClick={ this.handlePin }
className={ styles.image } />
/> <div className={ styles.content }>
<div className={ styles.title }> <Link to={ app.url === 'web' ? '/web' : `/${app.id}` } >
{ app.name } <DappIcon
</div> app={ app }
<div className={ styles.description }> className={ styles.image }
{ app.description } />
</div> <Header
<DappVouchFor as='h5'
app={ app } textAlign='center'
className={ styles.vouching } className={ styles.title }
maxNumber={ 10 } >
/> {app.name}
</Container> </Header>
</Link>
</div>
</div> </div>
); );
} }

View File

@ -41,15 +41,21 @@
} }
} }
.sectionTitle {
padding: 2em 0 0 4em !important;
margin-bottom: 0 !important;
}
.dapps { .dapps {
padding: 0 1.5em;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
margin: 1.5em 0; justify-content: center;
.dapp { .dapp {
flex: 0 0 25%; width: 12em;
max-width: 25%; margin-left: 2em;
min-width: 25%; margin-right: 2em;
} }
} }

View File

@ -45,48 +45,62 @@ class Dapps extends Component {
this.store.loadAllApps(); this.store.loadAllApps();
} }
render () { handlePin = (appId) => {
const { availability } = this.props; if (this.store.displayApps[appId].pinned) {
const applications = [].concat(this.store.visibleLocal, this.store.visibleViews, this.store.visibleBuiltin, this.store.visibleNetwork); this.store.unpinApp(appId);
} else {
this.store.pinApp(appId);
}
}
renderSection = (apps) => (
apps && apps.length > 0 &&
<div className={ styles.dapps }>
{
apps.map((app, index) => (
<DappCard
app={ app }
pinned={ this.store.displayApps[app.id] && this.store.displayApps[app.id].pinned }
availability={ this.props.availability }
className={ styles.dapp }
key={ `${index}_${app.id}` }
onPin={ this.handlePin }
/>
))
}
</div>
)
render () {
return ( return (
<Page className={ styles.dapps }> <Page className={ styles.layout }>
{this.renderSection(this.store.pinnedApps)}
{this.renderSection(this.store.visibleUnpinned)}
{ {
applications.map((app, index) => ( this.store.externalOverlayVisible &&
<DappCard (
app={ app } <div className={ styles.overlay }>
availability={ availability } <div>
className={ styles.dapp } <FormattedMessage
key={ `${index}_${app.id}` } id='dapps.external.warning'
/> defaultMessage='Applications made available on the network by 3rd-party authors are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each before interacting.'
)) />
}
{
this.store.externalOverlayVisible
? (
<div className={ styles.overlay }>
<div>
<FormattedMessage
id='dapps.external.warning'
defaultMessage='Applications made available on the network by 3rd-party authors are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each before interacting.'
/>
</div>
<div>
<Checkbox
className={ styles.accept }
label={
<FormattedMessage
id='dapps.external.accept'
defaultMessage='I understand that these applications are not affiliated with Parity'
/>
}
checked={ false }
onClick={ this.onClickAcceptExternal }
/>
</div>
</div> </div>
) <div>
: null <Checkbox
className={ styles.accept }
label={
<FormattedMessage
id='dapps.external.accept'
defaultMessage='I understand that these applications are not affiliated with Parity'
/>
}
checked={ false }
onClick={ this.onClickAcceptExternal }
/>
</div>
</div>
)
} }
</Page> </Page>
); );

View File

@ -17,46 +17,49 @@
import * as mobx from 'mobx'; import * as mobx from 'mobx';
import flatten from 'lodash.flatten'; import flatten from 'lodash.flatten';
import VisibleStore from '@parity/shared/lib/mobx/dappsStore'; import DappsStore from '@parity/shared/lib/mobx/dappsStore';
import RequestStore from './DappRequests/store'; import RequestStore from './DappRequests/store';
import methodGroups from './DappRequests/methodGroups'; import methodGroups from './DappRequests/methodGroups';
export default function execute (appId, method, params, callback) { export default function execute (appId, method, params, callback) {
const visibleStore = VisibleStore.get(); const dappsStore = DappsStore.get();
const requestStore = RequestStore.get(); const requestStore = RequestStore.get();
switch (method) { switch (method) {
case 'shell_getApps': case 'shell_getApps': {
const [displayAll] = params; const [displayAll] = params;
callback( callback(
null, null,
displayAll displayAll
? visibleStore.allApps.slice().map(mobx.toJS) ? dappsStore.allApps.slice().map(mobx.toJS)
: visibleStore.visibleApps.slice().map(mobx.toJS) : dappsStore.visibleApps.slice().map(mobx.toJS)
); );
return true; return true;
}
case 'shell_getFilteredMethods': case 'shell_getFilteredMethods': {
callback( callback(
null, null,
flatten(Object.keys(methodGroups).map(key => methodGroups[key].methods)) flatten(Object.keys(methodGroups).map(key => methodGroups[key].methods))
); );
return true; return true;
}
case 'shell_getMethodGroups': case 'shell_getMethodGroups': {
callback( callback(
null, null,
methodGroups methodGroups
); );
return true; return true;
}
case 'shell_getMethodPermissions': case 'shell_getMethodPermissions': {
callback(null, mobx.toJS(requestStore.permissions)); callback(null, mobx.toJS(requestStore.permissions));
return true; return true;
}
case 'shell_loadApp': case 'shell_loadApp': {
const [loadId, loadParams] = params; const [loadId, loadParams] = params;
const loadUrl = `/${loadId}/${loadParams || ''}`; const loadUrl = `/${loadId}/${loadParams || ''}`;
@ -64,27 +67,43 @@ export default function execute (appId, method, params, callback) {
callback(null, true); callback(null, true);
return true; return true;
}
case 'shell_requestNewToken': case 'shell_requestNewToken': {
callback(null, requestStore.createToken(appId)); callback(null, requestStore.createToken(appId));
return true; return true;
}
case 'shell_setAppVisibility': case 'shell_setAppVisibility': {
const [changeId, visibility] = params; const [appId, visibility] = params;
callback( callback(
null, null,
visibility visibility
? visibleStore.showApp(changeId) ? dappsStore.showApp(appId)
: visibleStore.hideApp(changeId) : dappsStore.hideApp(appId)
); );
return true; return true;
}
case 'shell_setMethodPermissions': case 'shell_setAppPinned': {
const [appId, pinned] = params;
callback(
null,
pinned
? dappsStore.pinApp(appId)
: dappsStore.unpinApp(appId)
);
return true;
}
case 'shell_setMethodPermissions': {
const [permissions] = params; const [permissions] = params;
callback(null, requestStore.setPermissions(permissions)); callback(null, requestStore.setPermissions(permissions));
return true; return true;
}
default: default:
return false; return false;