Jack Mallers
8 years ago
9 changed files with 469 additions and 12 deletions
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
@ -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%); |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue