Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI : Show considered UTXOs before performing transaction #807

Merged
43 changes: 41 additions & 2 deletions src/components/PaymentConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PropsWithChildren, useMemo } from 'react'
import { PropsWithChildren, useMemo, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import * as rb from 'react-bootstrap'
import Sprite from './Sprite'
Expand All @@ -10,6 +10,9 @@ import { AmountSats, BitcoinAddress } from '../libs/JmWalletApi'
import { jarInitial } from './jars/Jar'
import { isValidNumber } from '../utils'
import styles from './PaymentConfirmModal.module.css'
import { Utxos } from '../context/WalletContext'
import { SelectableUtxo, UtxoListDisplay } from './Send/ShowUtxos'
import Divider from './Divider'

const feeRange: (txFee: TxFee, txFeeFactor: number) => [number, number] = (txFee, txFeeFactor) => {
if (txFee.unit !== 'sats/kilo-vbyte') {
Expand Down Expand Up @@ -55,6 +58,37 @@ const useMiningFeeText = ({ tx_fees, tx_fees_factor }: Pick<FeeValues, 'tx_fees'
}, [t, tx_fees, tx_fees_factor])
}

type ReviewConsideredUtxosProps = {
utxos: SelectableUtxo[]
}
const ReviewConsideredUtxos = ({ utxos }: ReviewConsideredUtxosProps) => {
const { t } = useTranslation()
const settings = useSettings()
const [isOpen, setIsOpen] = useState<boolean>(false)

return (
<rb.Row className="mt-2">
<rb.Col xs={4} md={3} className="text-end">
<strong>{t('show_utxos.considered_utxos')}</strong>
</rb.Col>
<rb.Col xs={8} md={9}>
<Divider toggled={isOpen} onToggle={() => setIsOpen((current) => !current)} />
</rb.Col>
<rb.Collapse in={isOpen}>
<rb.Col xs={12} className="mt-2">
<UtxoListDisplay
utxos={utxos}
settings={settings}
onToggle={() => {
// No-op since these UTXOs are only for review and are not selectable
}}
/>
</rb.Col>
</rb.Collapse>
</rb.Row>
)
}

interface PaymentDisplayInfo {
sourceJarIndex?: JarIndex
destination: BitcoinAddress | string
Expand All @@ -64,6 +98,7 @@ interface PaymentDisplayInfo {
numCollaborators?: number
feeConfigValues?: FeeValues
showPrivacyInfo?: boolean
consideredUtxos?: Utxos
}

interface PaymentConfirmModalProps extends ConfirmModalProps {
Expand All @@ -80,6 +115,7 @@ export function PaymentConfirmModal({
numCollaborators,
feeConfigValues,
showPrivacyInfo = true,
consideredUtxos = [],
},
children,
...confirmModalProps
Expand All @@ -97,7 +133,7 @@ export function PaymentConfirmModal({

return (
<ConfirmModal {...confirmModalProps}>
<rb.Container className="mt-2">
<rb.Container className="mt-2" fluid>
{showPrivacyInfo && (
<rb.Row className="mt-2 mb-3">
<rb.Col xs={12} className="text-center">
Expand Down Expand Up @@ -207,6 +243,9 @@ export function PaymentConfirmModal({
</rb.Col>
</rb.Row>
)}
{consideredUtxos.length !== 0 && (
<ReviewConsideredUtxos utxos={consideredUtxos.map((it) => ({ ...it, checked: false, selectable: false }))} />
)}
{children && (
<rb.Row>
<rb.Col xs={12}>{children}</rb.Col>
Expand Down
5 changes: 3 additions & 2 deletions src/components/Send/ShowUtxos.module.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.utxoListDisplayHeight {
max-height: 17.6rem;
max-height: 15.7rem;
}

.row.row-normal {
Expand Down Expand Up @@ -31,6 +31,7 @@
height: 24px;
}

.checkbox:checked {
.checkbox:checked,
.checkbox:indeterminate {
accent-color: var(--bs-black);
}
54 changes: 28 additions & 26 deletions src/components/Send/ShowUtxos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,29 @@ interface UtxoRowProps {
utxo: SelectableUtxoTableRowData
onToggle: (utxo: SelectableUtxoTableRowData) => void
settings: Settings
showBackgroundColor: boolean
walletInfo: WalletInfo
t: TFunction
// TODO: remove
showBackgroundColor?: boolean
}

interface UtxoListDisplayProps {
utxos: SelectableUtxo[]
onToggle: (utxo: SelectableUtxo) => void
settings: Settings
showBackgroundColor: boolean
// TODO: remove
showBackgroundColor?: boolean
}

const UtxoRow = ({ utxo, onToggle, showBackgroundColor, settings, walletInfo, t }: UtxoRowProps) => {
const UtxoRow = ({ utxo, onToggle, settings, walletInfo, t }: UtxoRowProps) => {
const displayAddress = useMemo(() => shortenStringMiddle(utxo.address, 24), [utxo.address])
const tags = useMemo(() => utxoTags(utxo, walletInfo, t), [utxo, walletInfo, t])

return (
<Row
item={utxo}
className={classNames(styles.row, styles[`row-${tags[0].color || 'normal'}`], {
className={classNames(styles.row, styles[`row-${tags[0]?.color || 'normal'}`], {
[styles['row-frozen']]: utxo.frozen,
'bg-transparent': !showBackgroundColor,
'cursor-pointer': utxo.selectable,
'cursor-not-allowed': !utxo.selectable,
})}
Expand Down Expand Up @@ -110,25 +111,29 @@ const UtxoRow = ({ utxo, onToggle, showBackgroundColor, settings, walletInfo, t

type SelectableUtxoTableRowData = SelectableUtxo & Pick<TableTypes.TableNode, 'id'>

const UtxoListDisplay = ({ utxos, onToggle, settings, showBackgroundColor = true }: UtxoListDisplayProps) => {
const DEFAULT_UTXO_LIST_THEME = {
Table: `
--data-table-library_grid-template-columns: 2.5rem 2.5rem 17rem 3rem 12rem 1fr};
`,
BaseCell: `
padding: 0.35rem 0.25rem !important;
margin: 0.15rem 0px !important;
`,
Cell: `
&:nth-of-type(3) {
text-align: left;
}
&:nth-of-type(5) {
text-align: right;
}
`,
}

const UtxoListDisplay = ({ utxos, onToggle, settings }: UtxoListDisplayProps) => {
const { t } = useTranslation()
const walletInfo = useCurrentWalletInfo()

const TABLE_THEME = {
Table: `
--data-table-library_grid-template-columns: 2.5rem 2.5rem 17rem 3rem 12rem 1fr};
`,
BaseCell: `
padding: 0.35rem 0.25rem !important;
margin: 0.15rem 0px !important;
`,
Cell: `
&:nth-of-type(5) {
text-align: right;
}
`,
}
const tableTheme = useTheme(TABLE_THEME)
const tableTheme = useTheme(DEFAULT_UTXO_LIST_THEME)

const tableData: TableTypes.Data<SelectableUtxoTableRowData> = useMemo(
() => ({
Expand All @@ -155,7 +160,6 @@ const UtxoListDisplay = ({ utxos, onToggle, settings, showBackgroundColor = true
key={index}
utxo={utxo}
onToggle={onToggle}
showBackgroundColor={showBackgroundColor}
settings={settings}
walletInfo={walletInfo}
t={t}
Expand All @@ -169,7 +173,7 @@ const UtxoListDisplay = ({ utxos, onToggle, settings, showBackgroundColor = true
)
}

type SelectableUtxo = Utxo & { checked: boolean; selectable: boolean }
export type SelectableUtxo = Utxo & { checked: boolean; selectable: boolean }

// TODO: rename to QuickFreezeUtxosModal?
const ShowUtxos = ({ isOpen, onCancel, onConfirm, isLoading, utxos, alert }: ShowUtxosProps) => {
Expand Down Expand Up @@ -241,7 +245,6 @@ const ShowUtxos = ({ isOpen, onCancel, onConfirm, isLoading, utxos, alert }: Sho
)
}}
settings={settings}
showBackgroundColor={true}
/>
</rb.Row>
{upperUtxos.length > 0 && lowerUtxos.length > 0 && (
Expand All @@ -261,7 +264,6 @@ const ShowUtxos = ({ isOpen, onCancel, onConfirm, isLoading, utxos, alert }: Sho
)
}}
settings={settings}
showBackgroundColor={true}
/>
</rb.Row>
</rb.Collapse>
Expand Down Expand Up @@ -290,4 +292,4 @@ const ShowUtxos = ({ isOpen, onCancel, onConfirm, isLoading, utxos, alert }: Sho
)
}

export { ShowUtxos }
export { ShowUtxos, UtxoListDisplay }
23 changes: 0 additions & 23 deletions src/components/Send/SourceJarSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ export const SourceJarSelector = ({
const reloadCurrentWalletInfo = useReloadCurrentWalletInfo()

const [showUtxos, setShowUtxos] = useState<ShowUtxosProps>()
//const [unFrozenUtxos, setUnFrozenUtxos] = useState<Utxos>([])
//const [frozenUtxos, setFrozenUtxos] = useState<Utxos>([])

const jarBalances = useMemo(() => {
if (!walletInfo) return []
Expand All @@ -51,27 +49,6 @@ export const SourceJarSelector = ({
)
}, [walletInfo])

/*useEffect(() => {
if (frozenUtxos.length === 0 && unFrozenUtxos.length === 0) {
return
}
const frozenUtxosToUpdate = frozenUtxos.filter((utxo: Utxo) => utxo.checked && !utxo.locktime)
const timeLockedUtxo = frozenUtxos.find((utxo: Utxo) => utxo.checked && utxo.locktime)
const allUnFrozenUnchecked = unFrozenUtxos.every((utxo: Utxo) => !utxo.checked)

if (frozenUtxos.length > 0 && timeLockedUtxo) {
setAlert({ variant: 'danger', message: `${t('show_utxos.alert_for_time_locked')} ${timeLockedUtxo.locktime}` })
} else if (
(frozenUtxos.length > 0 || unFrozenUtxos.length > 0) &&
allUnFrozenUnchecked &&
frozenUtxosToUpdate.length === 0
) {
setAlert({ variant: 'warning', message: t('show_utxos.alert_for_unfreeze_utxos'), dismissible: true })
} else {
setAlert(undefined)
}
}, [frozenUtxos, unFrozenUtxos, t, setAlert])*/

const handleUtxosFrozenState = useCallback(
async (selectedUtxos: Utxos) => {
if (!showUtxos) return
Expand Down
4 changes: 3 additions & 1 deletion src/components/Send/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { useLoadConfigValue } from '../../context/ServiceConfigContext'
import { useWaitForUtxosToBeSpent } from '../../hooks/WaitForUtxosToBeSpent'
import { routes } from '../../constants/routes'
import { JM_MINIMUM_MAKERS_DEFAULT } from '../../constants/config'

import { initialNumCollaborators } from './helpers'

const INITIAL_DESTINATION = null
Expand Down Expand Up @@ -515,6 +514,9 @@ export default function Send({ wallet }: SendProps) {
isCoinjoin: showConfirmSendModal.isCoinJoin,
numCollaborators: showConfirmSendModal.numCollaborators!,
feeConfigValues: { ...feeConfigValues, tx_fees: showConfirmSendModal.txFee },
consideredUtxos: (walletInfo?.utxosByJar[showConfirmSendModal.sourceJarIndex!] || [])
.filter((utxo) => !utxo.frozen)
.sort((a, b) => a.confirmations - b.confirmations),
}}
/>
)}
Expand Down
8 changes: 8 additions & 0 deletions src/components/jar_details/UtxoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ const TABLE_THEME = {
`,
BaseCell: `
padding: 0.25rem 0.25rem !important;

input[type="checkbox"] {
width: 16px;
height: 16px;
}
input:checked, input:indeterminate {
accent-color: var(--bs-black);
}
&:nth-of-type(1) {
text-align: center;
}
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@
},
"show_utxos": {
"select_utxos": "Select UTXOs",
"selected_utxos": "Selected UTXOs",
"considered_utxos": "Considered UTXOs",
"show_utxo_title": "Select UTXOs to be considered",
"show_utxo_subtitle": "The following UTXOs are considered in the transaction. Every unselected UTXO will be frozen and can be unfrozen later on.",
"show_utxo_subtitle_when_allutxos_are_frozen": "The following UTXOs are frozen. Please select them to be considered in the transaction."
Expand Down