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:
parent
144eb0ce49
commit
5baa66150b
|
@ -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,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
node_modules/
|
||||
build/
|
||||
build_webpack/
|
||||
build_storybook/
|
||||
build/contracts/
|
||||
truffle-config.js
|
||||
gnosis-safe-contracts/
|
||||
.DS_Store
|
|
@ -0,0 +1,3 @@
|
|||
import '@storybook/addon-actions/register'
|
||||
import '@storybook/addon-links/register'
|
||||
import '@storybook/addon-knobs/register'
|
|
@ -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);
|
|
@ -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">
|
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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: [
|
||||
|
|
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
@ -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",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/*eslint-disable*/
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
// Load environment variables from .env file. Suppress warnings using silent
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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 />)
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// @flow
|
||||
import Button from 'material-ui/Button'
|
||||
|
||||
export default Button
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
.link {
|
||||
text-decoration: none;
|
||||
color: $secondary;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 />)
|
|
@ -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,
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue