Skip to content

Commit

Permalink
Enable Reclaiming of Escrowed Payments (#10)
Browse files Browse the repository at this point in the history
Allows for the reclaiming of escrowed payments. Once the payment has expired, it gives the sender a notification on the wallet to either remind the sender through a text message, or allows the sender to reclaim the payment.
  • Loading branch information
medhakothari authored and jmrossy committed Aug 2, 2019
1 parent a20d5d2 commit 1d17771
Show file tree
Hide file tree
Showing 24 changed files with 277 additions and 112 deletions.
1 change: 1 addition & 0 deletions packages/mobile/locales/en-US/global.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"sendPaymentFailed": "Failure sending payment",
"paymentRequestFailed": "Payment Request did not send",
"reclaimingEscrowedPaymentFailed": "Payment could not be reclaimed.",
"fetchingReclaimFeeFailed": "Error fetching suggested fee.",
"connectingToCelo": "Connecting to the Celo Network...",
"poorConnection": {
"0": "Bad Connection",
Expand Down
1 change: 1 addition & 0 deletions packages/mobile/locales/es-AR/global.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"sendPaymentFailed": "Falla en el envío de pago",
"paymentRequestFailed": "No se pudo enviar la solicitud de pago",
"reclaimingEscrowedPaymentFailed": "El pago no pudo ser reclamado.",
"fetchingReclaimFeeFailed": "Error al obtener la tarifa sugerida.",
"connectingToCelo": "Conectando a la red de Celo...",
"poorConnection": {
"0": "Mala Conexión",
Expand Down
1 change: 1 addition & 0 deletions packages/mobile/src/app/ErrorMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum ErrorMessages {
SEND_PAYMENT_FAILED = 'sendPaymentFailed',
PAYMENT_REQUEST_FAILED = 'paymentRequestFailed',
RECLAIMING_ESCROWED_PAYMENT_FAILED = 'reclaimingEscrowedPaymentFailed',
FETCH_RECLAIM_FEE_FAILED = 'fetchingReclaimFeeFailed',
EXCHANGE_RATE_FAILED = 'exchangeFlow9:errorRefreshingRate',
EXCHANGE_RATE_CHANGE = 'exchangeFlow9:exchangeRateChange',
REDEEM_INVITE_FAILED = 'inviteFlow11:redeemFailed',
Expand Down
12 changes: 7 additions & 5 deletions packages/mobile/src/escrow/EscrowedPaymentLineItem.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import fontStyles from '@celo/react-components/styles/fonts'
import { getContactPhoneNumber } from '@celo/utils/src/contacts'
import * as React from 'react'
import { StyleSheet, Text } from 'react-native'
import { EscrowedPayment } from 'src/escrow/actions'
import { divideByWei, getMoneyDisplayValue } from 'src/utils/formatting'

interface Props {
payment: EscrowedPayment
}

export default function EscrowedPaymentLineItem(props: Props) {
const { message, recipient } = props.payment
const recipientPhoneNumber =
typeof recipient === 'string' ? recipient : getContactPhoneNumber(recipient)
const { amount, message, recipientPhone } = props.payment
return (
<Text numberOfLines={1} ellipsizeMode="middle" style={styles.oneLine}>
<Text style={[fontStyles.subSmall]}>
{recipientPhoneNumber} - {message}
{recipientPhone} - {message}
</Text>
<Text style={[fontStyles.subSmall, fontStyles.semiBold]}>
{' '}
${getMoneyDisplayValue(divideByWei(amount.toString()))}
</Text>
</Text>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as renderer from 'react-test-renderer'
import ReclaimPaymentConfirmationCard from 'src/escrow/ReclaimPaymentConfirmationCard'
import {} from 'src/home/NotificationBox'
import { createMockStore } from 'test/utils'
import { mockContactWithPhone } from 'test/values'
import { mockE164Number, mockRecipient } from 'test/values'

const store = createMockStore()

Expand All @@ -15,7 +15,8 @@ describe('ReclaimPaymentConfirmationCard', () => {
const tree = renderer.create(
<Provider store={store}>
<ReclaimPaymentConfirmationCard
recipient={mockContactWithPhone}
recipientPhone={mockE164Number}
recipientContact={mockRecipient}
amount={new BigNumber(10)}
currency={CURRENCY_ENUM.DOLLAR}
fee={new BigNumber(0.01)}
Expand Down
43 changes: 22 additions & 21 deletions packages/mobile/src/escrow/ReclaimPaymentConfirmationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ import PhoneNumberWithFlag from '@celo/react-components/components/PhoneNumberWi
import colors from '@celo/react-components/styles/colors'
import { fontStyles } from '@celo/react-components/styles/fonts'
import { componentStyles } from '@celo/react-components/styles/styles'
import { getContactPhoneNumber } from '@celo/utils/src/contacts'
import BigNumber from 'bignumber.js'
import * as React from 'react'
import { withNamespaces, WithNamespaces } from 'react-i18next'
import { StyleSheet, Text, View } from 'react-native'
import { MinimalContact } from 'react-native-contacts'
import { connect } from 'react-redux'
import componentWithAnalytics from 'src/analytics/wrapper'
import { CURRENCIES, CURRENCY_ENUM } from 'src/geth/consts'
import { Namespaces } from 'src/i18n'
import Logo from 'src/icons/Logo'
import { RootState } from 'src/redux/reducers'
import FeeIcon from 'src/send/FeeIcon'
import { getCurrencyColor, getMoneyDisplayValue } from 'src/utils/formatting'
import { getCurrencyColor, getMoneyDisplayValue, roundedUpNumber } from 'src/utils/formatting'
import { RecipientWithContact } from 'src/utils/recipient'

interface LineItemProps {
currencySymbol: string
Expand Down Expand Up @@ -52,7 +51,8 @@ function LineItemRow({
}

export interface OwnProps {
recipient: MinimalContact | string
recipientPhone: string
recipientContact?: RecipientWithContact
amount: BigNumber
comment?: string
fee?: BigNumber
Expand Down Expand Up @@ -85,7 +85,7 @@ class ReclaimPaymentConfirmationCard extends React.PureComponent<Props> {
<LineItemRow currencySymbol={'$'} amount={total} title={t('totalSent')} />
<LineItemRow
currencySymbol={currencySymbol}
amount={fee}
amount={roundedUpNumber(fee)}
title={t('securityFee')}
titleIcon={<FeeIcon />}
negative={true}
Expand All @@ -101,7 +101,15 @@ class ReclaimPaymentConfirmationCard extends React.PureComponent<Props> {
}

render() {
const { recipient, amount, comment, fee, currency, defaultCountryCode } = this.props
const {
recipientPhone,
recipientContact,
amount,
comment,
fee,
currency,
defaultCountryCode,
} = this.props
const currencySymbol = CURRENCIES[currency].symbol
const currencyColor = getCurrencyColor(currency)
return (
Expand All @@ -119,22 +127,15 @@ class ReclaimPaymentConfirmationCard extends React.PureComponent<Props> {
</View>
<View style={style.horizontalLine} />
<View style={style.details}>
{typeof recipient !== 'string' && (
{recipientContact && (
<Text style={[fontStyles.bodySmallSemiBold, style.contactName]}>
{recipient.displayName}
{recipientContact.displayName}
</Text>
)}
{typeof recipient !== 'string' ? (
<PhoneNumberWithFlag
e164PhoneNumber={getContactPhoneNumber(recipient) || ''}
defaultCountryCode={defaultCountryCode}
/>
) : (
<PhoneNumberWithFlag
e164PhoneNumber={recipient}
defaultCountryCode={defaultCountryCode}
/>
)}
<PhoneNumberWithFlag
e164PhoneNumber={recipientPhone}
defaultCountryCode={defaultCountryCode}
/>
{!!comment && <Text style={[fontStyles.bodySecondary, style.comment]}>{comment}</Text>}
</View>
{this.renderFeeAndTotal(amount, currencySymbol, fee)}
Expand Down Expand Up @@ -167,16 +168,16 @@ const style = StyleSheet.create({
feeContainer: {
marginTop: 10,
marginBottom: 10,
justifyContent: 'center',
alignItems: 'stretch',
},
feeRow: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'flex-start',
},
lineItemRow: {
flex: 1,
flexDirection: 'row',
alignItems: 'stretch',
justifyContent: 'space-between',
},
totalTitle: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { SHORT_CURRENCIES } from 'src/geth/consts'
import { createMockNavigationProp, createMockStore } from 'test/utils'
import { mockAccount, mockAccount2, mockContactWithPhone } from 'test/values'

const store = createMockStore()
const store = createMockStore({ escrow: { suggestedFee: '.01' } })

describe('ReclaimPaymentConfirmationScreen', () => {
it('renders correctly', () => {
Expand Down
50 changes: 36 additions & 14 deletions packages/mobile/src/escrow/ReclaimPaymentConfirmationScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CURRENCY_ENUM } from '@celo/utils/src/currencies'
import BigNumber from 'bignumber.js'
import * as React from 'react'
import { withNamespaces, WithNamespaces } from 'react-i18next'
import { StyleSheet, View } from 'react-native'
import { ActivityIndicator, StyleSheet, View } from 'react-native'
import { NavigationInjectedProps } from 'react-navigation'
import { connect } from 'react-redux'
import { showError } from 'src/alert/actions'
Expand All @@ -16,21 +16,25 @@ import { ErrorMessages } from 'src/app/ErrorMessages'
import { ERROR_BANNER_DURATION } from 'src/config'
import { EscrowedPayment, reclaimPayment } from 'src/escrow/actions'
import ReclaimPaymentConfirmationCard from 'src/escrow/ReclaimPaymentConfirmationCard'
import { reclaimSuggestedFeeSelector } from 'src/escrow/reducer'
import { Namespaces } from 'src/i18n'
import { navigate, navigateBack } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { navigateBack } from 'src/navigator/NavigationService'
import { RootState } from 'src/redux/reducers'
import { getSuggestedFeeDollars } from 'src/send/selectors'
import { isAppConnected } from 'src/redux/selectors'
import DisconnectBanner from 'src/shared/DisconnectBanner'
import { divideByWei } from 'src/utils/formatting'
import Logger from 'src/utils/Logger'
import { currentAccountSelector } from 'src/web3/selectors'

const TAG = 'escrow/ReclaimPaymentConfirmationScreen'

interface StateProps {
isReclaiming: boolean
e164PhoneNumber: string
account: string | null
fee: BigNumber
fee: string | null
dollarBalance: BigNumber
appConnected: boolean
}

interface DispatchProps {
Expand All @@ -45,9 +49,12 @@ const mapDispatchToProps = {

const mapStateToProps = (state: RootState): StateProps => {
return {
isReclaiming: state.escrow.isReclaiming,
e164PhoneNumber: state.account.e164PhoneNumber,
account: currentAccountSelector(state),
fee: getSuggestedFeeDollars(state),
fee: reclaimSuggestedFeeSelector(state),
dollarBalance: new BigNumber(state.stableToken.balance || 0),
appConnected: isAppConnected(state),
}
}

Expand All @@ -64,6 +71,10 @@ class ReclaimPaymentConfirmationScreen extends React.Component<Props> {
return reclaimPaymentInput
}

getFee = () => {
return this.props.fee || ''
}

onConfirm = async () => {
const escrowedPayment = this.getReclaimPaymentInput()
CeloAnalytics.track(CustomEventNames.escrowed_payment_reclaimed_by_sender)
Expand All @@ -79,8 +90,6 @@ class ReclaimPaymentConfirmationScreen extends React.Component<Props> {
this.props.showError(ErrorMessages.RECLAIMING_ESCROWED_PAYMENT_FAILED, ERROR_BANNER_DURATION)
return
}

navigate(Screens.WalletHome)
}

onPressEdit = () => {
Expand All @@ -94,28 +103,41 @@ class ReclaimPaymentConfirmationScreen extends React.Component<Props> {
return <ReviewHeader title={title} />
}

renderFooter = () => {
return this.props.isReclaiming ? (
<ActivityIndicator size="large" color={colors.celoGreen} />
) : null
}

render() {
const { t, fee } = this.props
const { t, isReclaiming, appConnected } = this.props
const payment = this.getReclaimPaymentInput()
const convertedAmount = divideByWei(payment.amount.valueOf())
const convertedFee = divideByWei(this.getFee().valueOf())

const currentBalance = this.props.dollarBalance
const userHasEnough = convertedFee.isLessThanOrEqualTo(currentBalance)

return (
<View style={styles.container}>
<DisconnectBanner />
<ReviewFrame
HeaderComponent={this.renderHeader}
FooterComponent={this.renderFooter}
confirmButton={{
action: this.onConfirm,
text: t('global:confirm'),
disabled: false,
disabled: isReclaiming || !userHasEnough || !appConnected,
}}
modifyButton={{ action: this.onPressEdit, text: t('cancel'), disabled: false }}
modifyButton={{ action: this.onPressEdit, text: t('cancel'), disabled: isReclaiming }}
>
<ReclaimPaymentConfirmationCard
recipient={payment.recipient}
recipientPhone={payment.recipientPhone}
recipientContact={payment.recipientContact}
comment={payment.message}
amount={payment.amount}
amount={convertedAmount}
currency={CURRENCY_ENUM.DOLLAR} // User can only request in Dollars
fee={fee}
fee={convertedFee}
/>
</ReviewFrame>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ exports[`ReclaimPaymentConfirmationCard renders correctly for send payment confi
]
}
>
Alice The Person
John Doe
</Text>
<View
style={
Expand Down Expand Up @@ -173,13 +173,15 @@ exports[`ReclaimPaymentConfirmationCard renders correctly for send payment confi
}
}
>
(209) 555-9790
(415) 555-0000
</Text>
</View>
</View>
<View
style={
Object {
"alignItems": "stretch",
"justifyContent": "center",
"marginBottom": 10,
"marginTop": 10,
}
Expand All @@ -188,8 +190,6 @@ exports[`ReclaimPaymentConfirmationCard renders correctly for send payment confi
<View
style={
Object {
"alignItems": "stretch",
"flex": 1,
"flexDirection": "row",
"justifyContent": "space-between",
}
Expand Down Expand Up @@ -248,8 +248,6 @@ exports[`ReclaimPaymentConfirmationCard renders correctly for send payment confi
<View
style={
Object {
"alignItems": "stretch",
"flex": 1,
"flexDirection": "row",
"justifyContent": "space-between",
}
Expand Down Expand Up @@ -349,8 +347,6 @@ exports[`ReclaimPaymentConfirmationCard renders correctly for send payment confi
<View
style={
Object {
"alignItems": "stretch",
"flex": 1,
"flexDirection": "row",
"justifyContent": "space-between",
}
Expand Down
Loading

0 comments on commit 1d17771

Please sign in to comment.