-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Send): Fee breakdown table (#606)
* Add `AccordionInfo` component * Add fee breakdown UI * Wrap `loadFeeConfigValues` in hook I'm not sure about the correctness of using useEffect inside the useFeeConfigValues hook, in particular regarding the effectiveness of the cleanup function (including `abortCtrl.abort()`) * Wrap mining fee text function in hook * Wrap estimated max collaborator fee in hook * Get real fee values * Fix TS error "TFunction is not generic" * Improve clarity * Fix popover header visibility in dark mode * Use passive voice in "cannot estimate mining fee" text Co-authored-by: Thebora Kompanioni <[email protected]> * Fix past usage in "cannot estimate mining fee" text * Fix popover dark mode colors * Align UI to the new proposal from editwentyone * ui: de-emphasize non-effective fee card content * chore: fix browser warning for fee breakdown subtitle validateDOMNesting(...): <a> cannot appear as a descendant of <a>. * chore(wording): Fee Estimation -> Maximum collaborator fee limit * chore: use css class text-small for fee breakdown labels * chore: use useMemo in FeeBreakdown component * feat(FeeBreakdown): open fee modal on click * ui(FeeBreakdown): add less-or-equal sign * ui(FeeBreakdown): show max collaborator fee value in title * ui(FeeBreakdown): open cj fee section by default * ui(FeeSettings): switch position of mining and collaborator fees * ui(FeeBreakdown): now works with sweep transactions * ui(FeeBreakdown): market maker fees -> collaborator fees * fix(actions): temporarily print linter debug messages * build(deps): update to prettier v2.8.7 * ui(FeeBreakdown): emphasize collaborator fee note * fix(FeeBreakdown): improve handling of missing config values * chore(FeeBreakdown): Remove unused `AccordionInfo` * revert: temporarily print linter debug messages --------- Co-authored-by: Thebora Kompanioni <[email protected]> Co-authored-by: theborakompanioni <[email protected]>
- Loading branch information
1 parent
c64a393
commit 7a6b920
Showing
16 changed files
with
500 additions
and
211 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,3 @@ | |
text-decoration: none; | ||
color: var(--bs-body-color); | ||
} | ||
|
||
.small-text { | ||
font-size: 0.8rem; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -130,7 +130,3 @@ | |
width: 100%; | ||
} | ||
} | ||
|
||
.text-small { | ||
font-size: 0.8rem; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import { useMemo, PropsWithChildren } from 'react' | ||
import { Trans, useTranslation } from 'react-i18next' | ||
import classNames from 'classnames' | ||
import Balance from '../Balance' | ||
import * as rb from 'react-bootstrap' | ||
import { useSettings } from '../../context/SettingsContext' | ||
import { SATS, formatSats, factorToPercentage } from '../../utils' | ||
import { FeeValues } from '../../hooks/Fees' | ||
import { AmountSats } from '../../libs/JmWalletApi' | ||
|
||
interface FeeBreakdownProps { | ||
feeConfigValues?: FeeValues | ||
numCollaborators: number | null | ||
amount: number | null | ||
onClick?: () => void | ||
} | ||
|
||
type FeeCardProps = { | ||
amount: AmountSats | null | ||
feeConfigValue: number | undefined | ||
highlight: boolean | ||
subtitle?: React.ReactNode | ||
onClick?: () => void | ||
} | ||
const FeeCard = ({ amount, feeConfigValue, highlight, subtitle, onClick }: FeeCardProps) => { | ||
const settings = useSettings() | ||
const { t } = useTranslation() | ||
|
||
return ( | ||
<rb.Card onClick={onClick} border={highlight ? (settings.theme === 'dark' ? 'light' : 'dark') : undefined}> | ||
<rb.Card.Body | ||
className={classNames('text-center py-2', { | ||
'text-muted': !highlight, | ||
})} | ||
> | ||
<div className="fs-5"> | ||
{feeConfigValue === undefined ? ( | ||
t('send.fee_breakdown.placeholder_config_value_not_present') | ||
) : ( | ||
<> | ||
{amount === null ? ( | ||
t('send.fee_breakdown.placeholder_amount_missing_amount') | ||
) : ( | ||
<> | ||
≤ | ||
<Balance convertToUnit={SATS} valueString={amount.toString()} showBalance={true} /> | ||
</> | ||
)} | ||
</> | ||
)} | ||
</div> | ||
<div className="text-secondary text-small">{subtitle}</div> | ||
</rb.Card.Body> | ||
</rb.Card> | ||
) | ||
} | ||
|
||
const FeeBreakdown = ({ | ||
feeConfigValues, | ||
numCollaborators, | ||
amount, | ||
onClick = () => {}, | ||
}: PropsWithChildren<FeeBreakdownProps>) => { | ||
const { t } = useTranslation() | ||
|
||
/** eg: "0.03%" */ | ||
const maxSettingsRelativeFee = useMemo( | ||
() => | ||
feeConfigValues?.max_cj_fee_rel | ||
? `${factorToPercentage(feeConfigValues.max_cj_fee_rel)}%` | ||
: t('send.fee_breakdown.placeholder_config_value_not_present'), | ||
[feeConfigValues, t] | ||
) | ||
|
||
/** eg: 44658 (expressed in sats) */ | ||
const maxEstimatedRelativeFee = useMemo( | ||
() => | ||
feeConfigValues?.max_cj_fee_rel && numCollaborators && amount && amount > 0 | ||
? Math.ceil(amount * feeConfigValues.max_cj_fee_rel) * numCollaborators | ||
: null, | ||
[feeConfigValues, amount, numCollaborators] | ||
) | ||
|
||
/** eg: "8,636 sats" */ | ||
const maxSettingsAbsoluteFee = useMemo( | ||
() => | ||
feeConfigValues?.max_cj_fee_abs | ||
? `${formatSats(feeConfigValues.max_cj_fee_abs)} sats` | ||
: t('send.fee_breakdown.placeholder_config_value_not_present'), | ||
[feeConfigValues, t] | ||
) | ||
|
||
/** eg: 77724 (expressed in sats) */ | ||
const maxEstimatedAbsoluteFee = useMemo( | ||
() => | ||
feeConfigValues?.max_cj_fee_abs && numCollaborators ? feeConfigValues.max_cj_fee_abs * numCollaborators : null, | ||
[feeConfigValues, numCollaborators] | ||
) | ||
|
||
const isAbsoluteFeeHighlighted = useMemo( | ||
() => | ||
maxEstimatedAbsoluteFee && maxEstimatedRelativeFee ? maxEstimatedAbsoluteFee > maxEstimatedRelativeFee : false, | ||
[maxEstimatedAbsoluteFee, maxEstimatedRelativeFee] | ||
) | ||
|
||
const isRelativeFeeHighlighted = useMemo( | ||
() => | ||
maxEstimatedAbsoluteFee && maxEstimatedRelativeFee ? maxEstimatedRelativeFee > maxEstimatedAbsoluteFee : false, | ||
[maxEstimatedAbsoluteFee, maxEstimatedRelativeFee] | ||
) | ||
|
||
return ( | ||
<rb.Row className="mb-2"> | ||
<rb.Col> | ||
<rb.Form.Label | ||
className={classNames('mb-1', 'text-small', { | ||
'text-muted': !isAbsoluteFeeHighlighted, | ||
})} | ||
> | ||
{t('send.fee_breakdown.absolute_limit')} | ||
</rb.Form.Label> | ||
<FeeCard | ||
highlight={isAbsoluteFeeHighlighted} | ||
amount={maxEstimatedAbsoluteFee} | ||
feeConfigValue={feeConfigValues?.max_cj_fee_abs} | ||
subtitle={ | ||
<Trans | ||
i18nKey="send.fee_breakdown.fee_card_subtitle" | ||
components={{ | ||
1: <span className="text-decoration-underline link-secondary" />, | ||
}} | ||
values={{ | ||
numCollaborators, | ||
maxFee: maxSettingsAbsoluteFee, | ||
}} | ||
/> | ||
} | ||
onClick={onClick} | ||
/> | ||
</rb.Col> | ||
<rb.Col> | ||
<rb.Form.Label | ||
className={classNames('mb-1', 'text-small', { | ||
'text-muted': !isRelativeFeeHighlighted, | ||
})} | ||
> | ||
{t('send.fee_breakdown.relative_limit')} | ||
</rb.Form.Label> | ||
<FeeCard | ||
highlight={isRelativeFeeHighlighted} | ||
amount={maxEstimatedRelativeFee} | ||
feeConfigValue={feeConfigValues?.max_cj_fee_rel} | ||
subtitle={ | ||
<Trans | ||
i18nKey="send.fee_breakdown.fee_card_subtitle" | ||
components={{ | ||
1: <span className="text-decoration-underline link-secondary" />, | ||
}} | ||
values={{ | ||
numCollaborators, | ||
maxFee: maxSettingsRelativeFee, | ||
}} | ||
/> | ||
} | ||
onClick={onClick} | ||
/> | ||
</rb.Col> | ||
</rb.Row> | ||
) | ||
} | ||
|
||
export default FeeBreakdown |
Oops, something went wrong.