Browse Source

feature(form design): add peers reducer, payments reducer, design form

renovate/lint-staged-8.x
Jack Mallers 8 years ago
parent
commit
8e16cc5294
  1. 4
      app/reducers/index.js
  2. 58
      app/reducers/payment.js
  3. 50
      app/reducers/peers.js
  4. 2
      app/routes/activity/components/Activity.js
  5. 37
      app/routes/app/components/App.js
  6. 95
      app/routes/app/components/components/Form.js
  7. 211
      app/routes/app/components/components/Form.scss
  8. 12
      app/routes/app/components/components/Nav.js
  9. 12
      app/routes/app/containers/AppContainer.js

4
app/reducers/index.js

@ -4,6 +4,8 @@ import { routerReducer as router } from 'react-router-redux'
import ticker from './ticker'
import info from './info'
import balance from './balance'
import payment from './payment'
import peers from './peers'
import activity from './activity'
const rootReducer = combineReducers({
@ -11,6 +13,8 @@ const rootReducer = combineReducers({
ticker,
info,
balance,
payment,
peers,
activity
})

58
app/reducers/payment.js

@ -0,0 +1,58 @@
import { callApi } from '../api'
import { btcToSatoshis, btcToUsd } from '../utils/bitcoin'
// ------------------------------------
// Constants
// ------------------------------------
export const SET_AMOUNT = 'SET_AMOUNT'
export const SET_MESSAGE = 'SET_MESSAGE'
export const SET_PUBKEY = 'SET_PUBKEY'
// ------------------------------------
// Actions
// ------------------------------------
export function setAmount(amount) {
return {
type: SET_AMOUNT,
amount
}
}
export function setMessage(message) {
return {
type: SET_MESSAGE,
message
}
}
export function setPubkey(pubkey) {
return {
type: SET_PUBKEY,
pubkey
}
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[SET_AMOUNT]: (state, { amount }) => ({ ...state, amount }),
[SET_MESSAGE]: (state, { message }) => ({ ...state, message }),
[SET_PUBKEY]: (state, { pubkey }) => ({ ...state, pubkey })
}
// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
paymentLoading: false,
amount: '0',
message: '',
pubkey: ''
}
export default function paymentReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}

50
app/reducers/peers.js

@ -0,0 +1,50 @@
import { callApi } from '../api'
// ------------------------------------
// Constants
// ------------------------------------
export const GET_PEERS = 'GET_PEERS'
export const RECEIVE_PEERS = 'RECEIVE_PEERS'
// ------------------------------------
// Actions
// ------------------------------------
export function getPeers() {
return {
type: GET_PEERS
}
}
export function receivePeers({ peers }) {
return {
type: RECEIVE_PEERS,
peers
}
}
export const fetchPeers = () => async (dispatch) => {
dispatch(getPeers())
const peers = await callApi('peers')
dispatch(receivePeers(peers.data))
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[GET_PEERS]: (state) => ({ ...state, peersLoading: true }),
[RECEIVE_PEERS]: (state, { peers }) => ({ ...state, peersLoading: false, peers })
}
// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
peersLoading: false,
peers: []
}
export default function peersReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}

2
app/routes/activity/components/Activity.js

@ -29,7 +29,7 @@ class Activity extends Component {
<label className={`${styles.label} ${styles.input}`}>
<MdSearch />
</label>
<input className={`${styles.text} ${styles.input}`} placeholder='Search transactions by amount, pubkey, channel' type='text' />
<input className={`${styles.text} ${styles.input}`} placeholder='Search transactions by amount, public key, channel' type='text' />
</div>
<div className={styles.activities}>

37
app/routes/app/components/App.js

@ -1,9 +1,17 @@
// @flow
import React, { Component } from 'react'
import Form from './components/Form.js'
import Nav from './components/Nav.js'
import styles from './App.scss'
class App extends Component {
constructor(props, context) {
super(props, context)
this.state = {
form: false
}
}
componentWillMount() {
const { fetchTicker, fetchBalance } = this.props
@ -12,10 +20,35 @@ class App extends Component {
}
render() {
const { ticker, balance, children } = this.props
const {
ticker,
balance,
setAmount,
setMessage,
setPubkey,
payment,
fetchPeers,
peers,
children
} = this.props
return (
<div>
<Nav ticker={ticker} balance={balance} />
<Form
isOpen={this.state.form}
close={() => this.setState({ form: false })}
setAmount={setAmount}
setMessage={setMessage}
setPubkey={setPubkey}
payment={payment}
fetchPeers={fetchPeers}
peers={peers}
/>
<Nav
ticker={ticker}
balance={balance}
formClicked={(type) => this.setState({ form: true })}
/>
<div className={styles.content}>
{children}
</div>

95
app/routes/app/components/components/Form.js

@ -0,0 +1,95 @@
// @flow
import React, { Component } from 'react'
import { FaDollar, FaBitcoin } from 'react-icons/lib/fa'
import { MdArrowBack, MdClose } from 'react-icons/lib/md'
import styles from './Form.scss'
class Form extends Component {
componentWillMount() {
this.props.fetchPeers()
}
render() {
const {
setAmount,
setMessage,
setPubkey,
payment: { amount, message, pubkey },
peers: { peers },
isOpen,
close
} = this.props
return (
<div className={`${styles.formContainer} ${isOpen ? styles.open : ''}`}>
<div className={styles.container}>
<div className={styles.esc} onClick={close}>
<MdClose />
</div>
<div className={styles.content}>
<section className={styles.amountContainer}>
<label>
<FaBitcoin />
</label>
<input
type='text'
size=''
style={{ width: `${(amount.length * 20) + 10}%`, fontSize: `${190 - (amount.length ** 2)}px` }}
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</section>
<section className={styles.inputContainer}>
<label>For:</label>
<input
type='text'
placeholder='Dinner, Rent, etc'
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</section>
<section className={styles.inputContainer}>
<label>To:</label>
<input
type='text'
placeholder='Public key'
value={pubkey}
onChange={(e) => setPubkey(e.target.value)}
/>
</section>
<section className={styles.peersContainer}>
{
peers.length ?
<ul className={styles.peers}>
<h4>Connected Peers</h4>
{
peers.map(peer => {
console.log('peer: ', peer)
return(
<li key={peer.pub_key} className={styles.peer} onClick={() => setPubkey(peer.pub_key)}>
<p className={styles.address}>{peer.address}</p>
<p className={styles.pubkey}>{peer.pub_key}</p>
<MdArrowBack />
</li>
)
})
}
</ul>
:
null
}
</section>
<section className={styles.buttonGroup}>
<div className={styles.button}>Pay</div>
<div className={styles.button}>Request</div>
</section>
</div>
</div>
</div>
)
}
}
Form.propTypes = {}
export default Form

211
app/routes/app/components/components/Form.scss

@ -0,0 +1,211 @@
.formContainer {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100vh;
background: #fff;
z-index: 0;
opacity: 0;
transition: all 0.5s;
&.open {
opacity: 1;
z-index: 10;
}
}
.container {
position: relative;
height: 100vh;
margin: 5%;
}
.esc {
position: absolute;
top: 0;
right: 0;
color: #999999;
cursor: pointer;
padding: 20px;
border-radius: 50%;
&:hover {
color: #555459;
background: #E8E8E8;
}
&:active {
color: #fff;
background: #ebb864;
}
svg {
width: 32px;
height: 32px;
}
}
.content {
width: 50%;
margin: 0 auto;
display: flex;
flex-direction: column;
height: 100vh;
justify-content: center;
align-items: center;
.amountContainer {
color: #ebb864;
display: flex;
justify-content: center;
min-height: 120px;
margin-bottom: 20px;
label, input[type=text] {
color: inherit;
display: inline-block;
vertical-align: top;
padding: 0;
}
label {
svg {
width: 100px;
height: 100px;
}
}
input[type=text] {
width: 100px;
font-size: 180px;
border: none;
outline: 0;
-webkit-appearance: none;
}
}
.inputContainer {
width: 100%;
display: flex;
justify-content: center;
font-size: 18px;
height: auto;
min-height: 55px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 6px;
position: relative;
padding: 0 20px;
label, input[type=text] {
font-size: inherit;
}
label {
padding-top: 19px;
padding-bottom: 12px;
color: #ccc;
}
input[type=text] {
width: 100%;
border: none;
outline: 0;
-webkit-appearance: none;
height: 55px;
padding: 0 10px;
}
}
.peersContainer {
width: 100%;
margin: 10px 0 50px 0;
h4 {
text-transform: uppercase;
color: #000;
letter-spacing: 2;
font-weight: bold;
margin-bottom: 20px;
padding: 10px 0;
}
.peers {
overflow-y: scroll;
width: 100%;
}
.peer {
position: relative;
width: 100%;
border-top: 1px solid #ccc;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
border-left: 1px solid transparent;
padding: 5px;
cursor: pointer;
transition: all 0.25s;
&:hover {
background: lighten(#ebb864, 20%);
border-radius: 6px;
border: 1px solid #ebb864;
svg {
visibility: visible;
}
}
p {
margin: 3px 0;
}
.address {
font-size: 18px;
}
.pubkey {
font-size: 12px;
}
svg {
visibility: hidden;
position: absolute;
top: calc(50% - 8px);
right: 5%;
color: #000;
}
}
}
.buttonGroup {
width: 100%;
display: flex;
flex-direction: row;
border-radius: 6px;
overflow: hidden;
.button {
cursor: pointer;
height: 55px;
min-height: 55px;
text-transform: none;
font-size: 18px;
transition: opacity .2s ease-out;
background: #ebb864;
color: #fff;
border: none;
font-weight: 500;
padding: 0;
width: 100%;
max-width: 50%;
text-align: center;
line-height: 55px;
&:first-child {
border-right: 1px solid lighten(#ebb864, 20%);
}
}
}
}

12
app/routes/app/components/components/Nav.js

@ -9,7 +9,7 @@ import styles from './Nav.scss'
class Nav extends Component {
render() {
const { ticker, balance } = this.props
const { ticker, balance, formClicked } = this.props
return (
<nav className={styles.nav}>
<ul className={styles.info}>
@ -52,11 +52,8 @@ class Nav extends Component {
</li>
</ul>
<div className={styles.buttons}>
<div className={styles.button}>
<span>Pay</span>
</div>
<div className={styles.button}>
<span>Request</span>
<div className={styles.button} onClick={formClicked}>
<span>New</span>
</div>
</div>
</nav>
@ -66,7 +63,8 @@ class Nav extends Component {
Nav.propTypes = {
ticker: React.PropTypes.object.isRequired,
balance: React.PropTypes.object.isRequired
balance: React.PropTypes.object.isRequired,
formClicked: React.PropTypes.func.isRequired
}
export default Nav

12
app/routes/app/containers/AppContainer.js

@ -3,16 +3,24 @@ import App from '../components/App'
import { fetchTicker } from '../../../reducers/ticker'
import { fetchBalance } from '../../../reducers/balance'
import { fetchInfo } from '../../../reducers/info'
import { fetchPeers } from '../../../reducers/peers'
import { setAmount, setMessage, setPubkey } from '../../../reducers/payment'
const mapDispatchToProps = {
fetchTicker,
fetchBalance,
fetchInfo
fetchInfo,
fetchPeers,
setAmount,
setMessage,
setPubkey
}
const mapStateToProps = (state) => ({
ticker: state.ticker,
balance: state.balance
balance: state.balance,
payment: state.payment,
peers: state.peers
})
export default connect(mapStateToProps, mapDispatchToProps)(App)
Loading…
Cancel
Save