Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
Merge pull request #3687 from mrfelton/feat/probe-route
Browse files Browse the repository at this point in the history
Show payment route on probe summary
  • Loading branch information
mrfelton authored Nov 7, 2020
2 parents 1b64157 + 40fc4b9 commit c8721fb
Show file tree
Hide file tree
Showing 16 changed files with 213 additions and 102 deletions.
69 changes: 69 additions & 0 deletions renderer/components/Activity/PaymentModal/Htlc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Flex } from 'rebass/styled-components'
import { useIntl } from 'react-intl'
import { CoinBig } from '@zap/utils/coin'
import { getDisplayNodeName } from 'reducers/payment/utils'
import { Truncate } from 'components/Util'
import { Text } from 'components/UI'
import { CryptoSelector, CryptoValue } from 'containers/UI'
import ArrowRight from 'components/Icon/ArrowRight'
import messages from './messages'

const HtlcHops = ({ hops, ...rest }) => {
const { formatMessage, formatNumber } = useIntl()
return (
<Flex {...rest} flexDirection="column">
{hops.map(hop => {
const displayName = getDisplayNodeName(hop)
const hasFee = CoinBig(hop.feeMsat).gt(0)
return (
<Flex
key={hop.pubKey}
alignItems="center"
className="hint--top-left"
data-hint={formatMessage(
{ ...messages[hasFee ? 'htlc_hop_fee' : 'htlc_hop_no_fee'] },
{ hopFee: formatNumber(hop.feeMsat), cryptoUnitName: 'msat' }
)}
justifyContent="flex-end"
my={1}
>
<Flex alignItems="center" color="gray" mx={2}>
<ArrowRight />
</Flex>
<Truncate maxlen={50} text={displayName} />
</Flex>
)
})}
</Flex>
)
}

HtlcHops.propTypes = {
hops: PropTypes.array.isRequired,
}

const Htlc = ({ route, isAmountVisible = true, ...rest }) => {
const amountExcludingFees = CoinBig(route.totalAmt)
.minus(route.totalFees)
.toString()
return (
<Flex alignItems="center" justifyContent="space-between" {...rest}>
{isAmountVisible && (
<Text fontWeight="normal">
<CryptoValue value={amountExcludingFees} />
<CryptoSelector ml={2} />
</Text>
)}
<HtlcHops hops={route.hops} />
</Flex>
)
}

Htlc.propTypes = {
isAmountVisible: PropTypes.bool,
route: PropTypes.object.isRequired,
}

export default Htlc
16 changes: 5 additions & 11 deletions renderer/components/Activity/PaymentModal/PaymentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ class PaymentModal extends React.PureComponent {
const { item, intl, ...rest } = this.props
const memo = item && getTag(item.paymentRequest, 'description')
const htlcs = item.htlcs.filter(htlc => htlc.status === 'SUCCEEDED')
const isMpp = htlcs.length > 1
const isRouted = htlcs.filter(htlc => htlc.route.hops.length > 1).length > 0

return (
<Panel {...rest}>
Expand Down Expand Up @@ -111,16 +109,12 @@ class PaymentModal extends React.PureComponent {
}
/>

{(isMpp || isRouted) && (
<>
<Bar variant="light" />
<Bar variant="light" />

<DataRow
left={<FormattedMessage {...messages.htlc_title} />}
right={<Route htlcs={htlcs} />}
/>
</>
)}
<DataRow
left={<FormattedMessage {...messages.htlc_title} />}
right={<Route htlcs={htlcs} />}
/>
</Panel.Body>
</Panel>
)
Expand Down
70 changes: 4 additions & 66 deletions renderer/components/Activity/PaymentModal/Route.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Box, Flex } from 'rebass/styled-components'
import { useIntl } from 'react-intl'
import { CoinBig } from '@zap/utils/coin'
import { getDisplayNodeName } from 'reducers/payment/utils'
import { Truncate } from 'components/Util'
import { CryptoSelector, CryptoValue } from 'containers/UI'
import { Bar, Text } from 'components/UI'
import ArrowRight from 'components/Icon/ArrowRight'
import messages from './messages'

const HtlcHops = ({ hops, ...rest }) => {
const { formatMessage, formatNumber } = useIntl()
return (
<Flex {...rest} flexDirection="column">
{hops.map(hop => {
const displayName = getDisplayNodeName(hop)
const hasFee = CoinBig(hop.feeMsat).gt(0)
return (
<Flex
key={hop.pubKey}
alignItems="center"
className="hint--top-left"
data-hint={formatMessage(
{ ...messages[hasFee ? 'htlc_hop_fee' : 'htlc_hop_no_fee'] },
{ hopFee: formatNumber(hop.feeMsat), cryptoUnitName: 'msat' }
)}
justifyContent="flex-end"
my={1}
>
<Flex alignItems="center" color="gray" mx={2}>
<ArrowRight />
</Flex>
<Truncate maxlen={50} text={displayName} />
</Flex>
)
})}
</Flex>
)
}

HtlcHops.propTypes = {
hops: PropTypes.array.isRequired,
}

const Htlc = ({ htlc, isAmountVisible = true, ...rest }) => {
const amountExcludingFees = CoinBig(htlc.route.totalAmt)
.minus(htlc.route.totalFees)
.toString()
return (
<Flex alignItems="center" justifyContent="space-between" {...rest}>
{isAmountVisible && (
<Text fontWeight="normal">
<CryptoValue value={amountExcludingFees} />
<CryptoSelector ml={2} />
</Text>
)}
<HtlcHops hops={htlc.route.hops} />
</Flex>
)
}

Htlc.propTypes = {
htlc: PropTypes.object.isRequired,
isAmountVisible: PropTypes.bool.isRequired,
}
import { Box } from 'rebass/styled-components'
import { Bar } from 'components/UI'
import Htlc from './Htlc'

const Route = ({ htlcs, ...rest }) => {
return (
Expand All @@ -75,7 +13,7 @@ const Route = ({ htlcs, ...rest }) => {
return (
<React.Fragment key={htlc.attemptTimeNs + htlc.resolveTimeNs}>
{!isFirst && <Bar my={2} opacity={0.2} variant="light" />}
<Htlc htlc={htlc} isAmountVisible={isMpp} />
<Htlc isAmountVisible={isMpp} route={htlc.route} />
</React.Fragment>
)
})}
Expand Down
2 changes: 2 additions & 0 deletions renderer/components/Activity/PaymentModal/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export PaymentModal from './PaymentModal'
export Route from './Route'
export Htlc from './Htlc'
1 change: 1 addition & 0 deletions renderer/components/Pay/PaySummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const PaySummary = props => {
minFee={getMinFee(routes)}
mt={-3}
payReq={payReq}
route={routes[0]}
/>
)
}
Expand Down
13 changes: 10 additions & 3 deletions renderer/components/Pay/PaySummaryLightning.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import BigArrowRight from 'components/Icon/BigArrowRight'
import { Bar, DataRow, Link, Spinner, Text, Tooltip } from 'components/UI'
import { CryptoSelector, CryptoValue, FiatValue } from 'containers/UI'
import { Truncate } from 'components/Util'
import { Htlc } from 'components/Activity/PaymentModal'
import messages from './messages'

const ConfigLink = ({ feeLimit, openModal, ...rest }) => (
Expand All @@ -34,6 +35,7 @@ class PaySummaryLightning extends React.Component {
nodes: PropTypes.array,
openModal: PropTypes.func.isRequired,
payReq: PropTypes.string.isRequired,
route: PropTypes.object,
}

static defaultProps = {
Expand Down Expand Up @@ -110,6 +112,7 @@ class PaySummaryLightning extends React.Component {
minFee,
nodes,
payReq,
route,
...rest
} = this.props

Expand Down Expand Up @@ -157,9 +160,13 @@ class PaySummaryLightning extends React.Component {
</Text>
</Box>
<Box width={5 / 11}>
<Text className="hint--bottom-left" data-hint={payeeNodeKey} textAlign="right">
<Truncate maxlen={nodeAlias ? 30 : 15} text={nodeAlias || payeeNodeKey} />
</Text>
{route ? (
<Htlc route={route} />
) : (
<Text className="hint--bottom-left" data-hint={payeeNodeKey} textAlign="right">
<Truncate maxlen={nodeAlias ? 30 : 15} text={nodeAlias || payeeNodeKey} />
</Text>
)}
</Box>
</Flex>
</Box>
Expand Down
3 changes: 2 additions & 1 deletion renderer/containers/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from 'reducers/lnurl'
import { initBackupService } from 'reducers/backup'
import { infoSelectors } from 'reducers/info'
import { paySelectors } from 'reducers/pay'
import { setModals, modalSelectors } from 'reducers/modal'
import { fetchSuggestedNodes } from 'reducers/channels'
import { initTickers } from 'reducers/ticker'
Expand All @@ -26,7 +27,7 @@ const mapStateToProps = state => ({
activeWalletSettings: walletSelectors.activeWalletSettings(state),
isAppReady: appSelectors.isAppReady(state),
isSyncedToGraph: infoSelectors.isSyncedToGraph(),
redirectPayReq: state.pay.redirectPayReq,
redirectPayReq: paySelectors.redirectPayReq(state),
modals: modalSelectors.getModalState(state),
lnurlWithdrawParams: lnurlSelectors.lnurlWithdrawParams(state),
willShowLnurlAuthPrompt: lnurlSelectors.willShowLnurlAuthPrompt(state),
Expand Down
6 changes: 3 additions & 3 deletions renderer/containers/Channels/ChannelCreateForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { connect } from 'react-redux'
import ChannelCreateForm from 'components/Channels/ChannelCreateForm'
import { fetchTickers, tickerSelectors } from 'reducers/ticker'
import { openChannel } from 'reducers/channels'
import { queryFees } from 'reducers/pay'
import { queryFees, paySelectors } from 'reducers/pay'
import { balanceSelectors } from 'reducers/balance'
import { updateContactFormSearchQuery, contactFormSelectors } from 'reducers/contactsform'
import { walletSelectors } from 'reducers/wallet'
Expand All @@ -15,8 +15,8 @@ const mapStateToProps = state => ({
cryptoUnit: tickerSelectors.cryptoUnit(state),
walletBalance: balanceSelectors.walletBalanceConfirmed(state),
cryptoUnitName: tickerSelectors.cryptoUnitName(state),
isQueryingFees: state.pay.isQueryingFees,
onchainFees: state.pay.onchainFees,
isQueryingFees: paySelectors.isQueryingFees(state),
onchainFees: paySelectors.onchainFees(state),
lndTargetConfirmations: settingsSelectors.currentConfig(state).lndTargetConfirmations,
})

Expand Down
10 changes: 5 additions & 5 deletions renderer/containers/Pay.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import { Pay } from 'components/Pay'
import { fetchTickers, tickerSelectors } from 'reducers/ticker'
import { setRedirectPayReq, queryFees, queryRoutes } from 'reducers/pay'
import { setRedirectPayReq, queryFees, queryRoutes, paySelectors } from 'reducers/pay'
import { balanceSelectors } from 'reducers/balance'
import { addFilter } from 'reducers/activity'
import { channelsSelectors } from 'reducers/channels'
Expand All @@ -18,11 +18,11 @@ const mapStateToProps = state => ({
channelBalance: balanceSelectors.channelBalance(state),
cryptoUnit: tickerSelectors.cryptoUnit(state),
cryptoUnitName: tickerSelectors.cryptoUnitName(state),
isQueryingFees: state.pay.isQueryingFees,
isQueryingFees: paySelectors.isQueryingFees(state),
lndTargetConfirmations: settingsSelectors.currentConfig(state).lndTargetConfirmations,
redirectPayReq: state.pay.redirectPayReq,
onchainFees: state.pay.onchainFees,
routes: state.pay.routes,
redirectPayReq: paySelectors.redirectPayReq(state),
onchainFees: paySelectors.onchainFees(state),
routes: paySelectors.routes(state),
maxOneTimeSend: channelsSelectors.maxOneTimeSend(state),
walletBalanceConfirmed: balanceSelectors.walletBalanceConfirmed(state),
})
Expand Down
3 changes: 2 additions & 1 deletion renderer/containers/Pay/PaySummaryLightning.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { connect } from 'react-redux'
import PaySummaryLightning from 'components/Pay/PaySummaryLightning'
import { paySelectors } from 'reducers/pay'
import { settingsSelectors } from 'reducers/settings'
import { tickerSelectors } from 'reducers/ticker'
import { networkSelectors } from 'reducers/network'
import { openModal } from 'reducers/modal'

const mapStateToProps = state => ({
cryptoUnitName: tickerSelectors.cryptoUnitName(state),
isQueryingRoutes: state.pay.isQueryingRoutes,
isQueryingRoutes: paySelectors.isQueryingRoutes(state),
nodes: networkSelectors.nodes(state),
feeLimit: settingsSelectors.currentConfig(state).payments.feeLimit,
})
Expand Down
6 changes: 3 additions & 3 deletions renderer/containers/Pay/PaySummaryOnChain.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { connect } from 'react-redux'
import PaySummaryOnChain from 'components/Pay/PaySummaryOnChain'
import { tickerSelectors } from 'reducers/ticker'
import { queryFees } from 'reducers/pay'
import { queryFees, paySelectors } from 'reducers/pay'
import { networkSelectors } from 'reducers/network'

const mapStateToProps = state => ({
cryptoUnitName: tickerSelectors.cryptoUnitName(state),
isQueryingFees: state.pay.isQueryingFees,
isQueryingFees: paySelectors.isQueryingFees(state),
nodes: networkSelectors.nodes(state),
onchainFees: state.pay.onchainFees,
onchainFees: paySelectors.onchainFees(state),
})

const mapDispatchToProps = {
Expand Down
1 change: 1 addition & 0 deletions renderer/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import lnurl from './lnurl'
* @property {import('./invoice').State} invoice Invoice reducer.
* @property {import('./lnurl').State} lnurl Lnurl reducer.
* @property {import('./network').State} network Network reducer.
* @property {import('./pay').State} pay Pay reducer.
* @property {import('./payment').State} payment Payment reducer.
* @property {import('./settings').State} settings Settings reducer.
* @property {import('./transaction').State} transaction Transaction reducer.
Expand Down
1 change: 1 addition & 0 deletions renderer/reducers/pay/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import payReducer from './reducer'

export default payReducer
export paySelectors from './selectors'
export * from './constants'
export * from './reducer'
export * from './ipc'
12 changes: 12 additions & 0 deletions renderer/reducers/pay/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,22 @@ const {
SET_REDIRECT_PAY_REQ,
} = constants

/**
* @typedef State
* @property {boolean} isQueryingRoutes Boolean indicating if routes are being probed
* @property {boolean} isQueryingFees Boolean indicating if fees are being queried
* @property {{fast:string|null, medium:string|null, slow:string|null}} onchainFees Onchain fee rates
* @property {string|null} queryFeesError Query fees error message
* @property {string|null} queryRoutesError Query routes error message
* @property {string|null} redirectPayReq Payrequest injected from external source
* @property {object[]} routes Routes from last probe attempt
*/

// ------------------------------------
// Initial State
// ------------------------------------

/** @type {State} */
const initialState = {
isQueryingRoutes: false,
isQueryingFees: false,
Expand Down
Loading

0 comments on commit c8721fb

Please sign in to comment.