Skip to content

Commit

Permalink
refactor: send transfer fees to shared fee editor
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf committed Mar 4, 2025
1 parent b9205bb commit 0d1ec64
Show file tree
Hide file tree
Showing 20 changed files with 257 additions and 308 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('bitcoin-fees.utils', () => {
utxos: mockUtxos,
feeRate: 1,
});
expect(result).toBe(createMoney(141, 'BTC'));
expect(result).toStrictEqual(createMoney(141, 'BTC'));
});

it('returns null when calculation fails', () => {
Expand All @@ -42,7 +42,7 @@ describe('bitcoin-fees.utils', () => {
utxos: mockUtxos,
feeRate: 1,
});
expect(result).toBe(createMoney(110, 'BTC'));
expect(result).toStrictEqual(createMoney(110, 'BTC'));
});

it('returns null when calculation fails', () => {
Expand All @@ -64,7 +64,7 @@ describe('bitcoin-fees.utils', () => {
recipients: mockRecipients,
utxos: mockUtxos,
});
expect(result).toBeGreaterThan(0);
expect(result.amount.toNumber()).toBeGreaterThan(0);
});
});
});
16 changes: 8 additions & 8 deletions src/app/features/fee-editor/bitcoin/use-bitcoin-fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isUndefined } from '@leather.io/utils';

import type { TransferRecipient } from '@shared/models/form.model';

import type { RawFee, RawFees } from '../fee-editor.context';
import type { EditorFee, EditorFees } from '../fee-editor.context';
import { getApproximateFee, getBitcoinFee, getBitcoinSendMaxFee } from './bitcoin-fees.utils';

interface UseBitcoinFeesArgs {
Expand All @@ -28,7 +28,7 @@ export function useBitcoinFees({ amount, isSendingMax, recipients, utxos }: UseB
};
}, [satAmount, recipients, utxos]);

const rawFees = useMemo<RawFees>(() => {
const editorFees = useMemo<EditorFees>(() => {
if (isUndefined(feeRates)) return;

const determineUtxosForHighFeeArgs = {
Expand Down Expand Up @@ -72,29 +72,29 @@ export function useBitcoinFees({ amount, isSendingMax, recipients, utxos }: UseB
return {
slow: {
type: 'slow',
baseUnitFeeValue: lowFee,
feeRate: feeRates.hourFee.toNumber(),
feeValue: lowFee,
time: btcTxTimeMap.hourFee,
},
standard: {
type: 'standard',
baseUnitFeeValue: standardFee,
feeRate: feeRates.halfHourFee.toNumber(),
feeValue: standardFee,
time: btcTxTimeMap.halfHourFee,
},
fast: {
type: 'fast',
baseUnitFeeValue: highFee,
feeRate: feeRates.fastestFee.toNumber(),
feeValue: highFee,
time: btcTxTimeMap.fastestFee,
},
};
}, [feeRates, determineUtxosDefaultArgs, isSendingMax, recipients, utxos]);

return {
rawFees,
editorFees,
isLoading,
getCustomFeeData(feeRate: number): RawFee {
getCustomEditorFee(feeRate: number): EditorFee {
const determineUtxosForFeeArgs = {
...determineUtxosDefaultArgs,
feeRate,
Expand All @@ -105,8 +105,8 @@ export function useBitcoinFees({ amount, isSendingMax, recipients, utxos }: UseB

return {
type: 'custom',
baseUnitFeeValue: feeAsMoney,
feeRate,
feeValue: feeAsMoney,
time: '',
};
},
Expand Down
48 changes: 48 additions & 0 deletions src/app/features/fee-editor/components/current-fee-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { MarketData } from '@leather.io/models';
import { Approver, ItemLayout, Pressable } from '@leather.io/ui';

import type { EditorFee } from '@app/features/fee-editor/fee-editor.context';
import { formatEditorFeeItem } from '@app/features/fee-editor/fee-editor.utils';

import { CryptoAssetItemPlaceholder } from '../../../components/crypto-asset-item/crypto-asset-item-placeholder';
import { FeeItemIcon } from './fee-item-icon';

interface CurrentFeeItemProps {
currentFee: EditorFee | null;
isLoading: boolean;
marketData: MarketData;
onEditFee(): void;
}
export function CurrentFeeItem({
currentFee,
isLoading,
marketData,
onEditFee,
}: CurrentFeeItemProps) {
if (isLoading || !currentFee)
return (
<Approver.Section>
<Approver.Subheader>Fee</Approver.Subheader>
<CryptoAssetItemPlaceholder my="0" />
</Approver.Section>
);
const { titleLeft, captionLeft, titleRight, captionRight } = formatEditorFeeItem({
editorFee: currentFee,
marketData: marketData,
});
return (
<Approver.Section>
<Approver.Subheader>Fee</Approver.Subheader>
<Pressable onClick={onEditFee} mb="space.02">
<ItemLayout
img={<FeeItemIcon feeType={currentFee.type} />}
titleLeft={titleLeft}
captionLeft={captionLeft}
titleRight={titleRight}
captionRight={captionRight}
showChevron
/>
</Pressable>
</Approver.Section>
);
}
43 changes: 24 additions & 19 deletions src/app/features/fee-editor/components/custom-fee-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,37 @@ import { Stack } from 'leather-styles/jsx';

import { Button, Input, ItemLayout } from '@leather.io/ui';

import type { FeeItemProps } from './fee-item';
import { type EditorFee, useFeeEditorContext } from '../fee-editor.context';
import { formatEditorFeeItem } from '../fee-editor.utils';
import { FeeItemIcon } from './fee-item-icon';

interface CustomFeeItemProps extends FeeItemProps {
fee: string | null;
setFee(fee: string): void;
interface CustomFeeItemProps {
fee: EditorFee;
}

export function CustomFeeItem({
fee,
setFee,
feeType,
onSelect,
isSelected,
captionLeft,
titleRight,
captionRight,
}: CustomFeeItemProps) {
export function CustomFeeItem({ fee }: CustomFeeItemProps) {
const inputRef = useRef<HTMLInputElement>(null);
const {
customEditorFeeRate,
marketData,
selectedEditorFee,
onSetCustomEditorFeeRate,
onSetSelectedEditorFee,
} = useFeeEditorContext();

const { captionLeft, titleRight, captionRight } = formatEditorFeeItem({
editorFee: fee,
marketData,
});

const isSelected = selectedEditorFee?.type === fee.type;

return (
<Button
onClick={() => onSelect(feeType)}
onClick={() => onSetSelectedEditorFee(fee)}
variant="outline"
borderWidth={isSelected ? '2px' : '1px'}
borderColor={isSelected ? 'ink.border-selected' : 'ink.border-default'}
// Add margin compensation when not selected to maintain consistent size
// Add margin compensation to maintain consistent size
margin={isSelected ? '0px' : '1px'}
>
<ItemLayout
Expand Down Expand Up @@ -61,8 +65,9 @@ export function CustomFeeItem({
<Input.Root style={{ minHeight: '40px' }}>
<Input.Field
ref={inputRef}
onChange={e => setFee(e.target.value)}
value={fee ?? ''}
onChange={e => onSetCustomEditorFeeRate(e.target.value)}
placeholder="0"
value={customEditorFeeRate ?? ''}
/>
</Input.Root>
</Stack>
Expand Down
16 changes: 16 additions & 0 deletions src/app/features/fee-editor/components/default-fees-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useFeeEditorContext } from '../fee-editor.context';
import { CustomFeeItem } from './custom-fee-item';
import { FeeItem } from './fee-item';

export function DefaultFeesList() {
const { customEditorFeeRate, editorFees, getCustomEditorFee } = useFeeEditorContext();
if (!editorFees) return null;
return (
<>
<FeeItem fee={editorFees.slow} />
<FeeItem fee={editorFees.standard} />
<FeeItem fee={editorFees.fast} />
<CustomFeeItem fee={getCustomEditorFee(Number(customEditorFeeRate))} />
</>
);
}
6 changes: 3 additions & 3 deletions src/app/features/fee-editor/components/fee-item-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import {

import { IconWrapper } from '@app/components/icon-wrapper';

import type { FeeType } from '../fee-editor.context';
import type { EditorFeeType } from '../fee-editor.context';

const feeTypeToIconMap: Record<FeeType, ReactNode> = {
const feeTypeToIconMap: Record<EditorFeeType, ReactNode> = {
slow: <AnimalSnailIcon />,
standard: <AnimalRabbitIcon />,
fast: <AnimalEagleIcon />,
custom: <AnimalChameleonIcon />,
};

export function FeeItemIcon({ feeType }: { feeType: FeeType }) {
export function FeeItemIcon({ feeType }: { feeType: EditorFeeType }) {
const icon = feeTypeToIconMap[feeType] || null;

if (!icon) {
Expand Down
48 changes: 21 additions & 27 deletions src/app/features/fee-editor/components/fee-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,49 @@ import { Button, ItemLayout } from '@leather.io/ui';

import { FormError } from '@app/components/error/form-error';

import type { FeeType } from '../fee-editor.context';
import { type EditorFee, useFeeEditorContext } from '../fee-editor.context';
import { formatEditorFeeItem } from '../fee-editor.utils';
import { FeeItemIcon } from './fee-item-icon';

export interface FeeItemProps {
feeType: FeeType;
isSelected: boolean;
isInsufficientBalance: boolean;
onSelect(feeType: FeeType): void;
titleLeft: string;
captionLeft: string;
titleRight?: string;
captionRight?: string;
interface FeeItemProps {
fee: EditorFee;
}

export function FeeItem({
feeType,
onSelect,
isInsufficientBalance,
isSelected,
titleLeft,
captionLeft,
titleRight,
captionRight,
}: FeeItemProps) {
export function FeeItem({ fee }: FeeItemProps) {
const [isTouched, setIsTouched] = useState(false);
const { availableBalance, marketData, onSetSelectedEditorFee, selectedEditorFee } =
useFeeEditorContext();

const { titleLeft, captionLeft, titleRight, captionRight } = formatEditorFeeItem({
editorFee: fee,
marketData,
});

const isSelected = selectedEditorFee?.type === fee.type;
const isInsufficientBalance = availableBalance.amount.isLessThan(
selectedEditorFee?.feeValue?.amount ?? 0
);
const showInsufficientBalanceError = isTouched && isInsufficientBalance;

return (
<Button
onClick={() => {
setIsTouched(true);

if (isInsufficientBalance) return;

onSelect(feeType);
onSetSelectedEditorFee(fee);
}}
key={feeType}
key={fee.type}
variant="outline"
opacity={isInsufficientBalance ? 0.5 : 1}
borderWidth={isSelected ? '2px' : '1px'}
borderColor={
isSelected && !isInsufficientBalance ? 'ink.border-selected' : 'ink.border-default'
}
// Add margin compensation when not selected to maintain consistent size
// Add margin compensation to maintain consistent size
margin={isSelected ? '0px' : '1px'}
>
<Stack gap="0">
<ItemLayout
img={<FeeItemIcon feeType={feeType} />}
img={<FeeItemIcon feeType={fee.type} />}
titleLeft={titleLeft}
captionLeft={captionLeft}
titleRight={titleRight}
Expand Down
17 changes: 0 additions & 17 deletions src/app/features/fee-editor/components/fees.tsx

This file was deleted.

Loading

0 comments on commit 0d1ec64

Please sign in to comment.