Skip to content

Commit

Permalink
feat: payment confirm modal (#446)
Browse files Browse the repository at this point in the history
* feat: payment confirm modal

* dev: show success text in confirm modal if privacy is enabled

* dev: improved sweep text in confirm modal

* dev: display source jar in confirm modal

* review: improve sweep info in confirm modal
  • Loading branch information
theborakompanioni committed Aug 4, 2022
1 parent 2d9d13e commit 29eca37
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 1 deletion.
101 changes: 100 additions & 1 deletion src/components/Send.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState, useMemo } from 'react'
import React, { useEffect, useState, useMemo, useRef } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { Trans, useTranslation } from 'react-i18next'
import * as rb from 'react-bootstrap'
Expand All @@ -19,6 +19,7 @@ import * as Api from '../libs/JmWalletApi'
import { SATS, formatBtc, formatSats } from '../utils'
import { routes } from '../constants/routes'
import styles from './Send.module.css'
import { ConfirmModal } from './Modal'

const IS_COINJOIN_DEFAULT_VAL = true
// initial value for `minimum_makers` from the default joinmarket.cfg (last check on 2022-02-20 of v0.9.5)
Expand Down Expand Up @@ -297,6 +298,9 @@ export default function Send() {
[isInitializing, waitForUtxosToBeSpent]
)

const [showConfirmInputsModal, setShowConfirmInputsModal] = useState(false)
const submitButtonRef = useRef(null)

useEffect(() => {
setTakerStartedInfoAlert((current) => (isCoinjoinInProgress ? current : null))
}, [isCoinjoinInProgress])
Expand Down Expand Up @@ -542,10 +546,19 @@ export default function Send() {

if (isLoading || isOperationDisabled) return

setPaymentSuccessfulInfoAlert(null)

const form = e.currentTarget
const isValid = formIsValid

if (isValid) {
if (!showConfirmInputsModal) {
setShowConfirmInputsModal(true)
return
}

setShowConfirmInputsModal(false)

const counterparties = parseInt(numCollaborators, 10)

if (isSweep && amount !== 0) {
Expand Down Expand Up @@ -900,6 +913,7 @@ export default function Send() {
/>
)}
<rb.Button
ref={submitButtonRef}
variant={isCoinjoin ? 'dark' : 'danger'}
type="submit"
disabled={isOperationDisabled || isLoading || isSending || !formIsValid}
Expand All @@ -917,6 +931,91 @@ export default function Send() {
t('send.button_send_without_improved_privacy')
)}
</rb.Button>
<ConfirmModal
isShown={showConfirmInputsModal}
title={t('send.confirm_modal.title')}
onCancel={() => setShowConfirmInputsModal(false)}
onConfirm={() => {
submitButtonRef.current?.click()
}}
>
<rb.Container className="mt-2">
<rb.Row className="mt-2 mb-3">
<rb.Col xs={12} className="text-center">
{isCoinjoin ? (
<strong className="text-success">{t('send.confirm_modal.text_collaborative_tx_enabled')}</strong>
) : (
<strong className="text-danger">{t('send.confirm_modal.text_collaborative_tx_disabled')}</strong>
)}
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_source_jar')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start">
{t('send.confirm_modal.text_source_jar', {
jarId: `#${account}`,
})}
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_recipient')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start text-break slashed-zeroes">
{destination}
</rb.Col>
</rb.Row>
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_amount')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start">
{isSweep ? (
<div className="d-flex justify-content-start align-items-center">
<Trans i18nKey="send.confirm_modal.text_sweep_balance">
Sweep
<Balance
valueString={amountFieldValue().toString()}
convertToUnit={settings.unit}
showBalance={true}
/>
</Trans>
<rb.OverlayTrigger
placement="right"
overlay={
<rb.Popover>
<rb.Popover.Body>{t('send.confirm_modal.text_sweep_info_popover')}</rb.Popover.Body>
</rb.Popover>
}
>
<div className="d-inline-flex align-items-center">
<Sprite className={styles.infoIcon} symbol="info" width="13" height="13" />
</div>
</rb.OverlayTrigger>
</div>
) : (
<Balance
valueString={amountFieldValue().toString()}
convertToUnit={settings.unit}
showBalance={true}
/>
)}
</rb.Col>
</rb.Row>
{isCoinjoin && (
<rb.Row>
<rb.Col xs={3} className="text-end">
<strong>{t('send.confirm_modal.label_num_collaborators')}</strong>
</rb.Col>
<rb.Col xs={9} className="text-start">
{numCollaborators}
</rb.Col>
</rb.Row>
)}
</rb.Container>
</ConfirmModal>
</div>
</>
)
Expand Down
7 changes: 7 additions & 0 deletions src/components/Send.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ input[type='number'] {
filter: blur(2px);
}

.infoIcon {
margin: 2px 0 0 0.25rem;
color: var(--bs-gray-500);
border: 1px solid var(--bs-gray-500);
border-radius: 50%;
}

.accordion-button {
border: none;
width: 100%;
Expand Down
12 changes: 12 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@
"sweep_amount_breakdown_frozen_balance": "Frozen or locked balance",
"sweep_amount_breakdown_estimated_amount": "Estimated amount to be sent",
"sweep_amount_breakdown_explanation": "A sweep transaction will consume all UTXOs of a mixdepth leaving no coins behind except those that have been <1>frozen</1> or <3>time-locked</3>. Onchain transaction fees and market maker fees will be deducted from the amount so as to leave zero change. The exact transaction amount can only be calculated by JoinMarket at the point when the transaction is made. Therefore the estimated amount shown might deviate from the actually sent amount. Refer to the <5>JoinMarket documentation</5> for more details.",
"confirm_modal": {
"title": "Confirm payment",
"label_amount": "Amount",
"text_sweep_balance": "Sweep whole jar (<1></1>)",
"text_sweep_info_popover": "The exact transaction amount can only be calculated by JoinMarket at the point when the transaction is made.",
"label_recipient": "Recipient",
"label_source_jar": "Send from",
"text_source_jar": "Jar {{ jarId }}",
"label_num_collaborators": "Collaborators",
"text_collaborative_tx_enabled": "Payment with privacy improvement",
"text_collaborative_tx_disabled": "Payment without privacy improvement"
},
"coinjoin_precondition": {
"hint_missing_utxos": "To execute a collaborative transaction you need at least one UTXO with <2>{{ minConfirmations }}</2> confirmations in the source jar. Select another jar to send from or fund this jar and wait for <6>{{ minConfirmations }}</6> blocks.",
"hint_missing_confirmations": "A collaborative transaction requires one of your UTXOs to have <2>{{ minConfirmations }}</2> or more confirmations. Select another jar to send from or wait for <6>{{ amountOfMissingConfirmations }}</6> more block(s).",
Expand Down

0 comments on commit 29eca37

Please sign in to comment.