diff --git a/app/components/Channels/ChannelForm.js b/app/components/Channels/ChannelForm.js index 816cf48b..5334af6f 100644 --- a/app/components/Channels/ChannelForm.js +++ b/app/components/Channels/ChannelForm.js @@ -14,7 +14,7 @@ const ChannelForm = ({ form, setForm, ticker, peers, openChannel, currentTicker const pushamt = ticker.currency === 'usd' ? btc.btcToSatoshis(usd.usdToBtc(push_amt, currentTicker.price_usd)) : btc.btcToSatoshis(push_amt) openChannel({ pubkey: node_key, localamt, pushamt }) - setForm({ isOpen: false }) + // setForm({ isOpen: false }) } const customStyles = { diff --git a/app/components/Form/PayForm.js b/app/components/Form/PayForm.js index 3d93da5f..5f84cc71 100644 --- a/app/components/Form/PayForm.js +++ b/app/components/Form/PayForm.js @@ -63,7 +63,7 @@ class PayForm extends Component { isLn ? { width: '75%', fontSize: '85px' } : - { width: `${amount.length > 1 ? (amount.length * 15) - 5 : 25}%`, fontSize: `${190 - (amount.length ** 2)}px` } + { width: `${amount.length > 1 ? (amount.length * 20) - 5 : 25}%`, fontSize: `${190 - (amount.length ** 2)}px` } } value={currentAmount} onChange={event => setPayAmount(event.target.value)} diff --git a/app/components/GlobalError/GlobalError.js b/app/components/GlobalError/GlobalError.js new file mode 100644 index 00000000..6fffab17 --- /dev/null +++ b/app/components/GlobalError/GlobalError.js @@ -0,0 +1,24 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { MdClose } from 'react-icons/lib/md' +import styles from './GlobalError.scss' + +const GlobalError = ({ error, clearError }) => ( +
+
+
+ +
+

{error}

+
+
+) + + + +GlobalError.propTypes = { + error: PropTypes.string, + clearError: PropTypes.func.isRequired +} + +export default GlobalError diff --git a/app/components/GlobalError/GlobalError.scss b/app/components/GlobalError/GlobalError.scss new file mode 100644 index 00000000..5486c7fc --- /dev/null +++ b/app/components/GlobalError/GlobalError.scss @@ -0,0 +1,34 @@ +@import '../../variables.scss'; + +.container { + position: absolute; + z-index: 1001; + background: $red; + color: $white; + width: 100%; + text-align: center; + padding: 20px; + transition: all 0.25s ease; + + &.closed { + max-height: 0; + padding: 0; + } + + .content { + position: relative; + + .close { + position: absolute; + top: calc(50% - 8px); + right: 10%; + cursor: pointer; + } + + h2 { + font-size: 20px; + letter-spacing: 1.5px; + font-weight: bold; + } + } +} \ No newline at end of file diff --git a/app/components/GlobalError/index.js b/app/components/GlobalError/index.js new file mode 100644 index 00000000..41eb98ac --- /dev/null +++ b/app/components/GlobalError/index.js @@ -0,0 +1,3 @@ +import GlobalError from './GlobalError' + +export default GlobalError \ No newline at end of file diff --git a/app/lnd/methods/channelController.js b/app/lnd/methods/channelController.js index a508904b..dd328ad0 100644 --- a/app/lnd/methods/channelController.js +++ b/app/lnd/methods/channelController.js @@ -72,7 +72,8 @@ export function closeChannel(lnd, event, payload) { channel_point: { funding_txid: BufferUtil.hexToBuffer(tx), output_index: Number(payload.channel_point.output_index) - } + }, + force: true } return new Promise((resolve, reject) => diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js index ff26b1a7..0d0dd348 100644 --- a/app/lnd/methods/index.js +++ b/app/lnd/methods/index.js @@ -105,21 +105,30 @@ export default function (lnd, event, msg, data) { }) ) ) - .catch(error => console.log('addInvoice error: ', error)) + .catch(error => { + console.log('addInvoice error: ', error) + event.sender.send('invoiceFailed', { error: error.toString() }) + }) break case 'sendPayment': // Payment looks like { payment_preimage: Buffer, payment_route: Object } // { paymentRequest } = data paymentsController.sendPaymentSync(lnd, data) .then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route }))) - .catch(error => console.log('payinvoice error: ', error)) + .catch(error => { + console.log('payinvoice error: ', error) + event.sender.send('paymentFailed', { error: error.toString() }) + }) break case 'sendCoins': // Transaction looks like { txid: String } // { amount, addr } = data walletController.sendCoins(lnd, data) .then(({ txid }) => event.sender.send('transactionSuccessful', { amount: data.amount, addr: data.addr, txid })) - .catch(error => event.sender.send('transactionError', { error })) + .catch(error => { + console.log('error: ', error) + event.sender.send('transactionError', { error: error.toString() }) + }) break case 'openChannel': // Response is empty. Streaming updates on channel status and updates @@ -149,7 +158,10 @@ export default function (lnd, event, msg, data) { console.log('peer_id: ', peer_id) event.sender.send('connectSuccess', { pub_key: data.pubkey, address: data.host, peer_id }) }) - .catch(error => console.log('connectPeer error: ', error)) + .catch(error => { + event.sender.send('connectFailure', { error: error.toString() }) + console.log('connectPeer error: ', error) + }) break case 'disconnectPeer': // Empty response. Pass back pubkey on success to remove it from the peers list diff --git a/app/lnd/push/openchannel.js b/app/lnd/push/openchannel.js index a2df85b2..7bf8958a 100644 --- a/app/lnd/push/openchannel.js +++ b/app/lnd/push/openchannel.js @@ -5,7 +5,7 @@ export default function pushopenchannel(lnd, event, payload) { call.on('data', data => event.sender.send('pushchannelupdated', { data })) call.on('end', () => event.sender.send('pushchannelend')) - call.on('error', error => event.sender.send('pushchannelerror', { error })) + call.on('error', error => event.sender.send('pushchannelerror', { error: error.toString() })) call.on('status', status => event.sender.send('pushchannelstatus', { status })) resolve(null, payload) diff --git a/app/reducers/channels.js b/app/reducers/channels.js index b0a9f20c..863c4156 100644 --- a/app/reducers/channels.js +++ b/app/reducers/channels.js @@ -1,5 +1,6 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' +import { setError } from './error' // ------------------------------------ // Constants // ------------------------------------ @@ -89,21 +90,25 @@ export const channelSuccessful = () => (dispatch) => { // Receive IPC event for updated channel export const pushchannelupdated = () => (dispatch) => { + console.log('channelUpdatedData: ', channelUpdatedData) dispatch(fetchChannels()) } // Receive IPC event for channel end -export const pushchannelend = () => (dispatch) => { +export const pushchannelend = (event, channelEndData) => (dispatch) => { + console.log('channelEndData: ', channelEndData) dispatch(fetchChannels()) } // Receive IPC event for channel error -export const pushchannelerror = () => (dispatch) => { - dispatch(fetchChannels()) +export const pushchannelerror = (event, { error }) => (dispatch) => { + dispatch(openingFailure()) + dispatch(setError(error)) } // Receive IPC event for channel status -export const pushchannelstatus = () => (dispatch) => { +export const pushchannelstatus = (event, channelStatusData) => (dispatch) => { + console.log('channel Status data: ', channelStatusData) dispatch(fetchChannels()) } @@ -168,6 +173,8 @@ const ACTION_HANDLERS = { ), [OPENING_CHANNEL]: state => ({ ...state, openingChannel: true }), + [OPENING_FAILURE]: state => ({ ...state, openingChannel: false }), + [CLOSING_CHANNEL]: state => ({ ...state, closingChannel: true }) } diff --git a/app/reducers/error.js b/app/reducers/error.js new file mode 100644 index 00000000..8f3404fd --- /dev/null +++ b/app/reducers/error.js @@ -0,0 +1,45 @@ +// ------------------------------------ +// Initial State +// ------------------------------------ +const initialState = { + error: null +} + +// ------------------------------------ +// Constants +// ------------------------------------ +export const SET_ERROR = 'SET_ERROR' +export const CLEAR_ERROR = 'CLEAR_ERROR' + +// ------------------------------------ +// Actions +// ------------------------------------ +export function setError(error) { + return { + type: SET_ERROR, + error + } +} + +export function clearError() { + return { + type: CLEAR_ERROR + } +} + +// ------------------------------------ +// Action Handlers +// ------------------------------------ +const ACTION_HANDLERS = { + [SET_ERROR]: (state, { error }) => ({ ...state, error }), + [CLEAR_ERROR]: () => (initialState) +} + +// ------------------------------------ +// Reducer +// ------------------------------------ +export default function errorReducer(state = initialState, action) { + const handler = ACTION_HANDLERS[action.type] + + return handler ? handler(state, action) : state +} diff --git a/app/reducers/index.js b/app/reducers/index.js index 7761bece..20e0893b 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -18,6 +18,7 @@ import modal from './modal' import address from './address' import transaction from './transaction' import activity from './activity' +import error from './error' const rootReducer = combineReducers({ router, @@ -37,7 +38,8 @@ const rootReducer = combineReducers({ modal, address, transaction, - activity + activity, + error }) export default rootReducer diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index e151ccf8..795544c9 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -5,6 +5,7 @@ import { fetchBalance } from './balance' import { setFormType } from './form' import { setPayInvoice } from './payform' import { resetRequestForm } from './requestform' +import { setError } from './error' import { showNotification } from '../notifications' import { btc, usd } from '../utils' @@ -71,12 +72,6 @@ export function sendInvoice() { } } -export function invoiceFailed() { - return { - type: INVOICE_FAILED - } -} - // Send IPC event for a specific invoice export const fetchInvoice = payreq => (dispatch) => { dispatch(getInvoice()) @@ -117,6 +112,11 @@ export const createdInvoice = (event, invoice) => (dispatch) => { dispatch(resetRequestForm()) } +export const invoiceFailed = (event, { error }) => dispatch => { + dispatch({ type: INVOICE_FAILED }) + dispatch(setError(error)) +} + // Listen for invoice updates pushed from backend from subscribeToInvoices export const invoiceUpdate = (event, { invoice }) => (dispatch) => { dispatch({ type: UPDATE_INVOICE, invoice }) diff --git a/app/reducers/ipc.js b/app/reducers/ipc.js index a0789e30..0b3f7a8f 100644 --- a/app/reducers/ipc.js +++ b/app/reducers/ipc.js @@ -3,7 +3,7 @@ import { lndSyncing, lndSynced, lndStdout } from './lnd' import { receiveInfo } from './info' import { receiveAddress } from './address' import { receiveCryptocurrency } from './ticker' -import { receivePeers, connectSuccess, disconnectSuccess } from './peers' +import { receivePeers, connectSuccess, disconnectSuccess, connectFailure } from './peers' import { receiveChannels, @@ -21,8 +21,8 @@ import { } from './channels' import { lightningPaymentUri } from './payform' -import { receivePayments, paymentSuccessful } from './payment' -import { receiveInvoices, createdInvoice, receiveFormInvoice, invoiceUpdate } from './invoice' +import { receivePayments, paymentSuccessful, paymentFailed } from './payment' +import { receiveInvoices, createdInvoice, receiveFormInvoice, invoiceUpdate, invoiceFailed } from './invoice' import { receiveBalance } from './balance' import { receiveTransactions, @@ -48,6 +48,7 @@ const ipc = createIpc({ receiveInvoices, receiveInvoice: receiveFormInvoice, createdInvoice, + invoiceFailed, invoiceUpdate, receiveBalance, @@ -55,6 +56,7 @@ const ipc = createIpc({ lightningPaymentUri, paymentSuccessful, + paymentFailed, channelSuccessful, pushchannelupdated, @@ -68,6 +70,7 @@ const ipc = createIpc({ pushclosechannelstatus, connectSuccess, + connectFailure, disconnectSuccess, receiveAddress, diff --git a/app/reducers/payment.js b/app/reducers/payment.js index 795e88ea..a3907ca7 100644 --- a/app/reducers/payment.js +++ b/app/reducers/payment.js @@ -4,6 +4,7 @@ import { fetchBalance } from './balance' import { setFormType } from './form' import { resetPayForm } from './payform' import { showModal } from './modal' +import { setError } from './error' // ------------------------------------ // Constants @@ -47,12 +48,6 @@ export function paymentSuccessfull(payment) { } } -export function paymentFailed() { - return { - type: PAYMENT_FAILED - } -} - // Send IPC event for payments export const fetchPayments = () => (dispatch) => { dispatch(getPayments()) @@ -87,6 +82,11 @@ export const paymentSuccessful = () => (dispatch) => { dispatch(fetchBalance()) } +export const paymentFailed = (event, { error }) => (dispatch) => { + dispatch({ type: PAYMENT_FAILED }) + dispatch(setError(error)) +} + // ------------------------------------ // Action Handlers diff --git a/app/reducers/peers.js b/app/reducers/peers.js index e6aec70e..5b7060f7 100644 --- a/app/reducers/peers.js +++ b/app/reducers/peers.js @@ -1,5 +1,6 @@ import { createSelector } from 'reselect' import { ipcRenderer } from 'electron' +import { setError } from './error' // ------------------------------------ // Constants // ------------------------------------ @@ -27,12 +28,6 @@ export function connectPeer() { } } -export function connectFailure() { - return { - type: CONNECT_FAILURE - } -} - export function disconnectPeer() { return { type: DISCONNECT_PEER @@ -83,6 +78,12 @@ export const connectRequest = ({ pubkey, host }) => (dispatch) => { // Send IPC receive for successfully connecting to a peer export const connectSuccess = (event, peer) => dispatch => dispatch({ type: CONNECT_SUCCESS, peer }) +// Send IPC receive for unsuccessfully connecting to a peer +export const connectFailure = (event, { error }) => dispatch => { + dispatch({ type: CONNECT_FAILURE }) + dispatch(setError(error)) +} + // Send IPC send for disconnecting from a peer export const disconnectRequest = ({ pubkey }) => (dispatch) => { dispatch(disconnectPeer()) diff --git a/app/reducers/transaction.js b/app/reducers/transaction.js index 33821551..62b446e4 100644 --- a/app/reducers/transaction.js +++ b/app/reducers/transaction.js @@ -5,6 +5,7 @@ import { fetchBalance } from './balance' import { setFormType } from './form' import { resetPayForm } from './payform' import { showModal } from './modal' +import { setError } from './error' // ------------------------------------ // Constants @@ -67,8 +68,9 @@ export const transactionSuccessful = (event, { amount, addr, txid }) => (dispatc dispatch(resetPayForm()) } -export const transactionError = () => (dispatch) => { +export const transactionError = (event, { error }) => (dispatch) => { dispatch({ type: TRANSACTION_FAILED }) + dispatch(setError(error)) } // Listener for when a new transaction is pushed from the subscriber diff --git a/app/routes/activity/components/Activity.scss b/app/routes/activity/components/Activity.scss index 241e00c4..c7410ea9 100644 --- a/app/routes/activity/components/Activity.scss +++ b/app/routes/activity/components/Activity.scss @@ -92,6 +92,7 @@ .activityContainer { background: $white; transition: opacity 0.25s; + padding-bottom: 50px; &.pulldown { opacity: 0.15; diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js index 6d135d0b..f1485478 100644 --- a/app/routes/app/components/App.js +++ b/app/routes/app/components/App.js @@ -1,6 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import LndSyncing from 'components/LndSyncing' +import GlobalError from 'components/GlobalError' import LoadingBolt from 'components/LoadingBolt' import Form from 'components/Form' import ModalRoot from 'components/ModalRoot' @@ -40,6 +41,9 @@ class App extends Component { formProps, closeForm, + error: { error }, + clearError, + children } = this.props @@ -57,6 +61,7 @@ class App extends Component { return (
+ ({ @@ -67,6 +71,8 @@ const mapStateToProps = state => ({ invoice: state.invoice, modal: state.modal, + error: state.error, + currentTicker: tickerSelectors.currentTicker(state), isOnchain: payFormSelectors.isOnchain(state), isLn: payFormSelectors.isLn(state), diff --git a/webpack.config.renderer.dev.js b/webpack.config.renderer.dev.js index 02625bed..b76b554c 100644 --- a/webpack.config.renderer.dev.js +++ b/webpack.config.renderer.dev.js @@ -25,6 +25,7 @@ const publicPath = `http://localhost:${port}/dist`; const dll = path.resolve(process.cwd(), 'dll'); const manifest = path.resolve(dll, 'renderer.json'); +console.log('one') /** * Warn if the DLL is not built */ @@ -35,6 +36,8 @@ if (!(fs.existsSync(dll) && fs.existsSync(manifest))) { execSync('npm run build-dll'); } +console.log('two') + export default merge.smart(baseConfig, { devtool: 'inline-source-map', @@ -279,3 +282,5 @@ export default merge.smart(baseConfig, { } } }); + +console.log('three')