Landing Page (#9)

* Adding storybook

* Fixing lint problems

* Adding general styles and correct fonts to storybook

* Adding static storybook build

* Updating eslintrules to avoid react-router Link warnings

* Adding classnames package for css-in-modules and updating babel-loader

* Removing styles from .storybook config in favor of css-in-modules

* Adding file loader for images in webpack prod and dev config

* Adding footer color mui variable

* Creating landing page view

* Adding babel-jest

* Created Img and PageFrame layout components

* Created Block layout component

* Created Link layout component

* Updated styles veriable with constants

* Refactored Welcome view

* Adding vault icon and safe options(create and open) as buttons

* Using yarn pck manager and update deploy script including storybook

* Moving container to PageFrame and loading general styles in storybook

* Enabling React SPA in surge

* Improving look and feel of automatic github comment with links
This commit is contained in:
Adolfo Panizo 2018-03-15 11:14:20 +01:00 committed by GitHub
parent 144eb0ce49
commit 5baa66150b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 15147 additions and 163 deletions

View File

@ -34,7 +34,12 @@
"always", {
"annotationStyle": "line"
}
]
],
"jsx-a11y/anchor-is-valid": [ "error", {
"components": [ "Link" ],
"specialLink": [ "to", "hrefLeft", "hrefRight" ],
"aspects": [ "noHref", "invalidHref", "preferButton" ]
}]
},
"env": {
"jest/globals": true,

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
node_modules/
build/
build_webpack/
build_storybook/
build/contracts/
truffle-config.js
gnosis-safe-contracts/
.DS_Store

3
.storybook/addons.js Normal file
View File

@ -0,0 +1,3 @@
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'
import '@storybook/addon-knobs/register'

28
.storybook/config.js Normal file
View File

@ -0,0 +1,28 @@
import { addDecorator, configure } from '@storybook/react'
import { withKnobs } from '@storybook/addon-knobs'
import * as React from 'react'
import StoryRouter from 'storybook-router'
import 'App.scss'
(function (global) {
//Useful for adding data and libraries to window object.
})(typeof window !== 'undefined' ? window : {});
addDecorator(withKnobs);
addDecorator(StoryRouter())
addDecorator((story) => (
<div>
{ story() }
</div>
));
const components = require.context('../src/components', true, /\.stories\.((js|ts)x?)$/)
const routes = require.context('../src/routes', true, /\.stories\.((js|ts)x?)$/)
function loadStories() {
components.keys().forEach((filename) => components(filename))
routes.keys().forEach((filename) => routes(filename))
}
configure(loadStories, module);

View File

@ -0,0 +1,2 @@
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

View File

@ -0,0 +1,13 @@
process.env.NODE_ENV = 'development'
const prodConfig = require('../config/webpack.config.dev');
module.exports = function(storybookConfig, configType) {
const config = Object.assign({}, prodConfig);
storybookConfig.module.rules = storybookConfig.module.rules.concat(config.module.rules)
storybookConfig.resolve = config.resolve;
return storybookConfig;
};

View File

@ -4,18 +4,20 @@ node_js:
os:
- linux
before_script:
- npm install -g truffle
- npm install -g surge
- yarn global add truffle
- yarn global add surge
- git clone https://github.com/gnosis/gnosis-safe-contracts.git
- cd gnosis-safe-contracts
- truffle compile && cd ..
after_success:
- npm run build
- yarn build-storybook
- yarn build
- |
if [ ${TRAVIS_BRANCH} = "master" ]; then
export NODE_ENV=production;
else
export NODE_ENV=development;
fi
- cd build_webpack/ && cp index.html 200.html && cd ..
- chmod ugo+x ./config/deploy/deploy.sh
- ./config/deploy/deploy.sh

View File

@ -9,6 +9,7 @@ REPO_OWNER=${REPO_SLUG_ARRAY[0]}
REPO_NAME=${REPO_SLUG_ARRAY[1]}
DEPLOY_PATH=./build_webpack
DEPLOY_PATH_STORYBOOK=./build_storybook
DEPLOY_SUBDOMAIN_UNFORMATTED_LIST=()
@ -58,16 +59,18 @@ do
echo $DEPLOY_SUBDOMAIN
DEPLOY_DOMAIN=https://${DEPLOY_SUBDOMAIN}-${REPO_NAME}-${REPO_OWNER}.surge.sh
DEPLOY_STORYBOOK=https://storybook-${DEPLOY_SUBDOMAIN}-${REPO_NAME}-${REPO_OWNER}.surge.sh
surge --project ${DEPLOY_PATH} --domain $DEPLOY_DOMAIN;
surge --project ${DEPLOY_PATH_STORYBOOK} --domain $DEPLOY_STORYBOOK
if [ "$TRAVIS_PULL_REQUEST" != "false" ]
then
# Using the Issues api instead of the PR api
# Done so because every PR is an issue, and the issues api allows to post general comments,
# while the PR api requires that comments are made to specific files and specific commits
GITHUB_PR_COMMENTS=https://api.github.com/repos/${TRAVIS_REPO_SLUG}/issues/${TRAVIS_PULL_REQUEST}/comments
curl -H "Authorization: token ${GITHUB_API_TOKEN}" --request POST ${GITHUB_PR_COMMENTS} --data '{"body":"Travis automatic deployment: '${DEPLOY_DOMAIN}'"}'
curl -H "Authorization: token ${GITHUB_API_TOKEN}" --request POST ${GITHUB_PR_COMMENTS} --data '{"body":"Travis automatic deployment:\r\n '${DEPLOY_DOMAIN}' \r\n \r\n Storybook book automatic deployment: \r\n '${DEPLOY_STORYBOOK}'"}'
fi
done

View File

@ -1,3 +1,4 @@
/*eslint-disable*/
const autoprefixer = require('autoprefixer');
const cssvars = require('postcss-simple-vars');
const webpack = require('webpack');
@ -115,7 +116,7 @@ module.exports = {
},
},
],
},
},
{
test: /\.html$/,
use: [
@ -124,7 +125,17 @@ module.exports = {
options: { minimize: false }
}
]
}
},
{
test: /\.(jpe?g|png|svg)$/i,
exclude: /node_modules/,
use: [{
loader: "file-loader",
options: {
name: 'img/[hash].[ext]'
}
}]
},
]
},
plugins: [

View File

@ -149,6 +149,16 @@ module.exports = {
],
}),
},
{
test: /\.(jpe?g|png|svg)$/i,
exclude: /node_modules/,
use: [{
loader: "file-loader",
options: {
name: 'img/[hash].[ext]'
}
}]
},
],
},
plugins: [

2308
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,9 @@
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"precommit": "./precommit.sh",
"flow": "flow"
"flow": "flow",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook -o build_storybook"
},
"repository": {
"type": "git",
@ -34,13 +36,19 @@
"@babel/preset-flow": "^7.0.0-beta.40",
"@babel/preset-react": "^7.0.0-beta.40",
"@babel/preset-stage-0": "^7.0.0-beta.40",
"@storybook/addon-actions": "^3.3.15",
"@storybook/addon-knobs": "^3.3.15",
"@storybook/addon-links": "^3.3.15",
"@storybook/react": "^3.3.15",
"autoprefixer": "^8.1.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^7.2.3",
"babel-eslint": "8",
"babel-jest": "^22.4.1",
"babel-loader": "^8.0.0-beta.0",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.22.0",
"classnames": "^2.2.5",
"css-loader": "^0.28.10",
"detect-port": "^1.2.2",
"dotenv": "^5.0.1",
@ -65,12 +73,14 @@
"react": "^16.2.0",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.2.0",
"storybook-host": "^4.1.5",
"storybook-router": "^0.3.3",
"style-loader": "^0.20.2",
"truffle-contract": "^1.1.8",
"truffle-solidity-loader": "0.0.8",
"uglifyjs-webpack-plugin": "^1.2.2",
"web3": "0.18.4",
"webpack": "^4.0.0",
"webpack": "^4.1.1",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-cli": "^2.0.8",
"webpack-dev-server": "^3.1.0",

View File

@ -1,3 +1,4 @@
/*eslint-disable*/
process.env.NODE_ENV = 'development';
// Load environment variables from .env file. Suppress warnings using silent

View File

@ -1,6 +1,8 @@
// @flow
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import AppRoutes from './routes'
import './App.scss'
const ReactRouterApp = () => (
<BrowserRouter>

24
src/App.scss Normal file
View File

@ -0,0 +1,24 @@
html, body {
height: 100%;
width: 100%;
}
body {
font-family: 'Montserrat', sans-serif;
margin: 0;
}
body>div:first-child {
color: $secondary;
background-image: linear-gradient(to bottom, $primary, #1a829d, #1a829d, #1f5f76);
font-size: $regularFontSize;
min-height: 100%;
min-width: 100%;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
h1, h2, h3 {
font-family: 'Montserrat', sans-serif;
}

View File

@ -0,0 +1,21 @@
// @flow
import React from 'react'
import Block from '~/components/layout/Block'
import Link from '~/components/layout/Link'
import styles from './index.scss'
const Footer = () => (
<Block className={styles.footer}>
<Link to="/wallet">
Wallet
</Link>
<Link to="/transactions">
Transactions
</Link>
<Link to="/settings">
Settings
</Link>
</Block>
)
export default Footer

View File

@ -0,0 +1,8 @@
.footer {
padding: 0 20px;
display: grid;
grid-template-rows: 100%;
grid-template-columns: auto auto auto;
justify-content: end;
grid-column-gap: 20px;
}

View File

@ -0,0 +1,14 @@
// @flow
import { storiesOf } from '@storybook/react'
import * as React from 'react'
import { host } from 'storybook-host'
import Component from './index'
storiesOf('Components', module)
.addDecorator(host({
title: 'Footer',
align: 'center',
height: 250,
width: '100%',
}))
.add('Header', () => <Component />)

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Isolation_Mode" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 697.7 119.5" style="enable-background:new 0 0 697.7 119.5;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00A6C4;}
</style>
<g>
<g>
<path class="st0" d="M399.6,64.3v22.6c-6.2,4.7-15.4,8-23.8,8c-18.1,0-32.2-13.6-32.2-31.1c0-17.5,14.3-30.9,32.9-30.9
c8.8,0,17.5,3.3,23.6,8.6l-5.9,7.6c-4.7-4.4-11.3-7-17.7-7c-12.5,0-22.3,9.5-22.3,21.7c0,12.3,9.9,21.8,22.4,21.8
c4.6,0,9.7-1.6,14.1-4.2V64.3H399.6z"/>
<path class="st0" d="M461.2,94.4l-32.7-44v44h-10.3V33.1h10.2l32.9,44.1V33.1h10.2v61.3H461.2z"/>
<path class="st0" d="M551.4,63.7c0,17.5-14.2,31.1-32.5,31.1c-18.3,0-32.5-13.6-32.5-31.1c0-17.6,14.2-30.9,32.5-30.9
C537.3,32.8,551.4,46.2,551.4,63.7z M496.9,63.7c0,12.2,10.1,21.8,22.1,21.8c12,0,21.8-9.6,21.8-21.8c0-12.2-9.8-21.6-21.8-21.6
C506.9,42.1,496.9,51.6,496.9,63.7z"/>
<path class="st0" d="M608.1,38.9l-4.3,9.1c-6.7-4-13.3-5.7-18-5.7c-6,0-10,2.3-10,6.4c0,13.4,33.1,6.2,33,28.3
c0,11-9.6,17.7-23.1,17.7c-9.6,0-18.7-3.9-25.1-9.7l4.5-8.9c6.3,5.8,14.2,8.9,20.8,8.9c7.2,0,11.5-2.7,11.5-7.4
c0-13.7-33.1-6-33.1-27.9c0-10.5,9-17.1,22.3-17.1C594.6,32.6,602.4,35.2,608.1,38.9z"/>
<path class="st0" d="M624.9,94.4V33.1h10.3v61.3H624.9z"/>
<path class="st0" d="M696.9,38.9l-4.3,9.1c-6.7-4-13.3-5.7-18-5.7c-6,0-10,2.3-10,6.4c0,13.4,33.1,6.2,33,28.3
c0,11-9.6,17.7-23.1,17.7c-9.6,0-18.7-3.9-25.1-9.7l4.5-8.9c6.3,5.8,14.2,8.9,20.8,8.9c7.2,0,11.5-2.7,11.5-7.4
c0-13.7-33.1-6-33.1-27.9c0-10.5,9-17.1,22.3-17.1C683.4,32.6,691.2,35.2,696.9,38.9z"/>
</g>
<g>
<path class="st0" d="M200.7,29.9l-4.4,4.4c3.5,5.5,4.6,12.4,2.2,19c-4,11.2-16.4,17.1-27.7,13.1c-1.6-0.6-3.1-1.3-4.4-2.2
l-14.1,14.1L140,65.9c-5.7,3.8-13,4.9-19.9,2.5c-11.6-4.2-17.6-16.9-13.4-28.4c0.6-1.8,1.5-3.4,2.5-4.9l-5.3-5.3l-1,1.7
c-5.5,9-8.5,19.4-8.5,30.1c-0.1,31.9,25.9,58,57.8,58h0.1c31.9,0,57.8-25.9,57.9-57.8c0-10.6-2.9-21-8.4-30.1L200.7,29.9z"/>
<path class="st0" d="M115.7,41.6c-1.5,2-2.4,4.5-2.4,7.2c0,6.5,5.3,11.8,11.8,11.8c2.7,0,5.2-0.9,7.2-2.5L115.7,41.6z"/>
<g>
<path class="st0" d="M173.6,57.1c1.9,1.3,4.1,2,6.6,2c6.5,0,11.8-5.3,11.8-11.8c0-2.4-0.7-4.7-2-6.6L173.6,57.1z"/>
</g>
<g>
<path class="st0" d="M152.5,69.5l-47.8-48l1.9-2C118.4,7,134.5,0,151.8,0h0.1c17.5,0,34.3,7.5,46,20.5l1.8,2L152.5,69.5z
M112.5,21.5l40,40.1l39.5-39.3c-10.6-10.6-25-16.7-40-16.7h-0.1C137,5.6,123.1,11.2,112.5,21.5z"/>
</g>
<g>
<g>
<path class="st0" d="M99.8,100.4l-5.6-0.4c-23.8-1.5-56.6-7.4-77.8-26.5C3.3,61.8,0.8,50.7,0.7,50.2L0,46.8h86.6L86.1,50
c-0.4,2.9-0.6,5.9-0.6,8.8c0,13.2,3.8,26,11.1,36.9L99.8,100.4z"/>
</g>
<g>
<path class="st0" d="M204.6,100.4l3.1-4.7c7.3-10.9,11.1-23.6,11.1-36.7c0-3-0.2-6.1-0.6-9l-0.4-3.2h86.6l-0.7,3.4
c-0.1,0.5-2.6,11.6-15.6,23.3c-21.2,19.1-54.1,25-77.9,26.5L204.6,100.4z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,11 +1,19 @@
// @flow
import React from 'react'
import styles from './index.scss';
import Block from '~/components/layout/Block'
import Img from '~/components/layout/Img'
import { xl } from '~/theme/variables'
const logo = require('./gnosis_logo.svg')
const imgStyle = {
paddingTop: xl,
}
const Header = () => (
<header className={styles.header}>
<h1>GNOSIS DAPP BOILERPLATE</h1>
<div>Multisig 2.0</div>
</header>
<Block>
<Img src={logo} style={imgStyle} height={50} />
</Block>
)
export default Header

View File

@ -1,11 +0,0 @@
.header {
display: grid;
grid-template-columns: auto auto;
grid-column-gap: 20px;
align-items: center;
margin: 0 20px;
}
.subtitle {
justify-self: center;
}

View File

View File

@ -0,0 +1,34 @@
// @flow
import classNames from 'classnames/bind'
import React, { PureComponent } from 'react'
import styles from './index.scss'
const cx = classNames.bind(styles)
type Size = 'sm' | 'md' | 'xl' | 'xxl'
type Props = {
margin?: Size,
padding?: Size,
center?: boolean,
children: React.Node,
className?: string,
}
const capitalize = (value: Size) => value.charAt(0).toUpperCase() + value.slice(1)
class Block extends PureComponent<Props> {
render() {
const {
margin, padding, center, children, className, ...props
} = this.props
const paddingStyle = padding ? `padding${capitalize(padding)}` : undefined
return (
<div className={cx(className, margin, paddingStyle, { center })} {...props}>
{ children }
</div>
)
}
}
export default Block

View File

@ -0,0 +1,37 @@
.sm {
margin-bottom: 17px;
}
.md {
margin-bottom: 42px;
}
.xl {
margin-bottom: 96px;
}
.xxl {
margin-bottom: 200px;
}
.paddingSm {
padding: 0 $xs;
}
.paddingMd {
padding: 0 $md;
}
.paddingXl {
padding: 0 $xl;
}
.paddingXxl {
padding: 0 $xxl;
}
.center {
display: flex;
align-items: center;
justify-content: center;
}

View File

@ -0,0 +1,4 @@
// @flow
import Button from 'material-ui/Button'
export default Button

View File

@ -0,0 +1,26 @@
// @flow
import classNames from 'classnames/bind'
import React, { PureComponent } from 'react'
import styles from './index.scss'
const cx = classNames.bind(styles)
type Props = {
alt: string,
fullwidth?: boolean,
bordered?: boolean,
className?: string,
style?: React.object
}
class Img extends PureComponent<Props> {
render() {
const {
fullwidth, alt, bordered, className, style, ...props
} = this.props
return <img alt={alt} style={style} {...props} className={cx(styles.img, { fullwidth, bordered }, className)} />
}
}
export default Img

View File

@ -0,0 +1,14 @@
.img {
max-width: 100%;
box-sizing: border-box;
}
.bordered {
border: 1px solid #ddd;
}
.fullwidth {
padding: 0;
width: 40% !important;
margin: 0 60% 25px !important;
}

View File

@ -0,0 +1,17 @@
// @flow
import React from 'react'
import { Link } from 'react-router-dom'
import styles from './index.scss'
type Props = {
to: string,
children: React.Node,
}
const GnosisLink = ({ to, children, ...props }: Props) => (
<Link className={styles.link} to={to} {...props}>
{ children }
</Link>
)
export default GnosisLink

View File

@ -0,0 +1,4 @@
.link {
text-decoration: none;
color: $secondary;
}

View File

@ -1,12 +1,15 @@
// @flow
import React from 'react'
import Header from '../../Header'
import styles from './index.scss';
import styles from './index.scss'
const Page = ({children}) => (
<div className={styles.page}>
<Header />
type Props = {
children: React.Node,
}
const Page = ({ children }: Props) => (
<main className={styles.container}>
{children}
</div>
</main>
)
export default Page

View File

@ -1,10 +1,25 @@
// @flow
import React from 'react'
import styles from './index.scss';
import Block from '~/components/layout/Block'
import Page from '~/components/layout/Page'
import Footer from '~/components/Footer'
import Header from '~/components/Header'
import styles from './index.scss'
const PageFrame = ({ children }) => (
<main className={styles.container}>
{ children }
</main>
type Props = {
children: React.Node,
}
const PageFrame = ({ children }: Props) => (
<Block className={styles.container}>
<Block padding="xl" className={styles.frame}>
<Header />
<Page>
{children}
</Page>
<Footer />
</Block>
</Block>
)
export default PageFrame

View File

@ -1,3 +1,11 @@
.frame {
display: grid;
grid-template-rows: 50px auto 50px;
height: 100%;
align-items: center;
background-color: $tertiary;
}
.container {
margin: 0px 20px;
padding: $xxl;
}

View File

@ -15,9 +15,9 @@ const Transactions = Loadable({
const Routes = () => (
<Switch>
<Redirect exact from="/" to="/welcome" />
<Route exact path='/welcome' component={Welcome} />
<Route exact path='/transactions' component={Transactions} />
</Switch>
<Route exact path="/welcome" component={Welcome} />
<Route exact path="/transactions" component={Transactions} />
</Switch>
)
export default Routes

View File

@ -0,0 +1,121 @@
// @flow
/*eslint-disable*/
import Button from 'material-ui/Button'
import React, { Component } from 'react'
import { Form, Field } from 'react-final-form'
import { Link } from 'react-router-dom'
import contract from 'truffle-contract'
import TextField from '~/components/forms/TextField'
import Page from '~/components/layout/Page'
import PageFrame from '~/components/layout/PageFrame'
import getWeb3, { promisify } from '~/utils/getWeb3'
import Safe from '#/GnosisSafe.json'
import './App.scss'
class App extends Component {
constructor(props) {
super(props)
this.state = {
storageValue: 0,
web3: undefined,
safeAddress: undefined,
funds: undefined,
}
this.safe = contract(Safe)
}
componentWillMount() {
getWeb3.then((results) => {
const web3 = results.web3
this.safe.setProvider(web3.currentProvider)
this.setState({ web3 })
})
.catch(() => {
console.log('Error finding web3.')
})
}
onCallSafeContractSubmit = async () => {
try {
const web3 = this.state.web3
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
const safeInstance = await this.safe.new([accounts[0]], 1, 0, 0, { from: accounts[0], gas: '5000000' })
const transactionHash = safeInstance.transactionHash
// const transaction = await promisify(cb => web3.eth.getTransaction(transactionHash, cb))
// console.log("Transaction" + JSON.stringify(transaction, 2, 0))
const transactionReceipt = await promisify(cb => web3.eth.getTransactionReceipt(transactionHash, cb))
console.log(`Transaction Receipt${JSON.stringify(transactionReceipt, 2, 0)}`)
this.setState({ safeAddress: safeInstance.address })
} catch (error) {
console.log('Error while creating the Safe')
}
}
onAddFunds = async (values) => {
const fundsToAdd = values.funds
try {
const { web3, safeAddress } = this.state
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
const txData = { from: accounts[0], to: safeAddress, value: web3.toWei(fundsToAdd, 'ether') }
await promisify(cb => web3.eth.sendTransaction(txData, cb))
const funds = await promisify(cb => web3.eth.getBalance(safeAddress, cb))
const fundsInEther = funds ? web3.fromWei(funds.toNumber(), 'ether') : 0
this.setState({ funds: fundsInEther })
} catch (error) {
console.log(`Errog adding funds to safe${error}`)
}
}
render() {
const { safeAddress, funds } = this.state
return (
<Page>
<PageFrame>
<Form
onSubmit={this.onCallSafeContractSubmit}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<h2>Create a new Safe instance for testing purposes</h2>
<div>
<Button variant="raised" color="primary" type="submit">
Create Safe
</Button>
</div>
</form>
)}
/>
<Form
onSubmit={this.onAddFunds}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<h2>Add Funds to the safe</h2>
<div style={{ margin: '10px 0px' }}>
<label style={{ marginRight: '10px' }}>{safeAddress || 'Not safe detected'}</label>
</div>
{ safeAddress && <div>
<Field name="funds" component={TextField} type="text" placeholder="ETH to add" />
<Button type="submit" disabled={!safeAddress || pristine || invalid}>
Add funds
</Button>
</div> }
{ safeAddress && <div style={{ margin: '15px 0px' }}>
Total funds in this safe: { funds || 0 } ETH
</div> }
</form>
)}
/>
<Link to="/transactions">
<Button variant="raised" color="primary">
Go to transactions
</Button>
</Link>
</PageFrame>
</Page>
)
}
}
export default App

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,120 +1,32 @@
// @flow
import Button from 'material-ui/Button'
import React, { Component } from 'react'
import { Form, Field } from 'react-final-form'
import { Link } from 'react-router-dom'
import contract from 'truffle-contract'
import TextField from '~/components/forms/TextField'
import Page from '~/components/layout/Page'
import React from 'react'
import Block from '~/components/layout/Block'
import PageFrame from '~/components/layout/PageFrame'
import getWeb3, { promisify } from '~/utils/getWeb3'
import Safe from '#/GnosisSafe.json'
import './App.scss'
import Img from '~/components/layout/Img'
import Button from '~/components/layout/Button'
import Link from '~/components/layout/Link'
import styles from './Layout.scss'
class App extends Component {
constructor(props) {
super(props)
const vault = require('../assets/vault.svg')
this.state = {
storageValue: 0,
web3: undefined,
safeAddress: undefined,
funds: undefined,
}
const Welcome = () => (
<PageFrame>
<Block className={styles.safe}>
<Img src={vault} height={330} />
<Block className={styles.safeActions}>
<Link to="/transactions">
<Button variant="raised" color="primary">
Create a new Safe
</Button>
</Link>
<Link to="/transactions">
<Button variant="raised" color="primary">
Open a Safe
</Button>
</Link>
</Block>
</Block>
</PageFrame>
)
this.safe = contract(Safe)
}
componentWillMount() {
getWeb3.then(results => {
const web3 = results.web3
this.safe.setProvider(web3.currentProvider)
this.setState({web3})
})
.catch(() => {
console.log('Error finding web3.')
})
}
onCallSafeContractSubmit = async () => {
try {
const web3 = this.state.web3
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
const safeInstance = await this.safe.new([accounts[0]], 1, 0, 0, { from: accounts[0], gas: '5000000' })
const transactionHash = safeInstance.transactionHash
// const transaction = await promisify(cb => web3.eth.getTransaction(transactionHash, cb))
// console.log("Transaction" + JSON.stringify(transaction, 2, 0))
const transactionReceipt = await promisify(cb => web3.eth.getTransactionReceipt(transactionHash, cb))
console.log("Transaction Receipt" + JSON.stringify(transactionReceipt, 2, 0))
this.setState({ safeAddress: safeInstance.address})
} catch (error) {
console.log("Error while creating the Safe")
}
}
onAddFunds = async (values) => {
const fundsToAdd = values.funds
try {
const { web3, safeAddress } = this.state
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
const txData = {from: accounts[0], to: safeAddress, value: web3.toWei(fundsToAdd, 'ether')}
await promisify(cb => web3.eth.sendTransaction(txData, cb))
const funds = await promisify( cb => web3.eth.getBalance(safeAddress, cb))
const fundsInEther = funds ? web3.fromWei(funds.toNumber(), 'ether') : 0
this.setState({funds: fundsInEther})
} catch (error) {
console.log("Errog adding funds to safe" + error)
}
}
render() {
const { safeAddress, funds } = this.state
return (
<Page>
<PageFrame>
<Form
onSubmit={this.onCallSafeContractSubmit}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<h2>Create a new Safe instance for testing purposes</h2>
<div>
<Button variant="raised" color="primary" type="submit">
Create Safe
</Button>
</div>
</form>
)}
/>
<Form
onSubmit={this.onAddFunds}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<h2>Add Funds to the safe</h2>
<div style={{ margin: '10px 0px'}}>
<label style={{ marginRight: '10px' }}>{safeAddress ? safeAddress : 'Not safe detected'}</label>
</div>
{ safeAddress && <div>
<Field name="funds" component={TextField} type="text" placeholder="ETH to add" />
<Button type="submit" disabled={ !safeAddress || pristine || invalid}>
Add funds
</Button>
</div> }
{ safeAddress && <div style={{ margin: '15px 0px'}}>
Total funds in this safe: { funds ? funds : 0 } ETH
</div> }
</form>
)}
/>
<Link to="/transactions">
<Button variant="raised" color="primary">
Go to transactions
</Button>
</Link>
</PageFrame>
</Page>
);
}
}
export default App
export default Welcome

View File

@ -0,0 +1,18 @@
.safe {
display: grid;
justify-content: center;
grid-row-gap: $xxl;
}
.safeActions {
display: flex;
justify-content: space-around;
}
@media(max-width: $(screenXsMax)px) {
.safeActions {
grid-row-gap: $md;
display: grid;
justify-items: center;
}
}

View File

@ -0,0 +1,15 @@
// @flow
import { storiesOf } from '@storybook/react'
import * as React from 'react'
import { host } from 'storybook-host'
import { screenLg } from '~/theme/variables'
import Component from './Layout'
storiesOf('Routes', module)
.addDecorator(host({
title: 'Routes - Welcome',
align: 'center',
height: '100%',
width: `${screenLg}px`,
}))
.add('Welcome', () => <Component />)

View File

@ -1,7 +1,23 @@
var primary = '#1798cc'
var secondary = '#6b7c93'
// @flow
const primary = '#1798cc'
const secondary = '#13222b'
const tertiary = '#f6f9fc'
module.exports = Object.assign({}, {
primary,
secondary,
});
tertiary,
xs: '8px',
md: '16px',
xl: '24px',
xxl: '42px',
regularFontWeight: 400,
regularFontSize: '14px',
screenXs: 480,
screenXsMax: 767,
screenSm: 768,
screenSmMax: 991,
screenMd: 992,
screenMdMax: 1199,
screenLg: 1200,
})

12147
yarn.lock Normal file

File diff suppressed because it is too large Load Diff