Skip to content

Commit 1081d25

Browse files
authored
Merge pull request #438 from dkackman/default-fee
Default fee
2 parents 1bde7ea + 5e47f00 commit 1081d25

15 files changed

+150
-94
lines changed

src/components/AssignNftDialog.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
FormLabel,
2626
FormMessage,
2727
} from './ui/form';
28-
import { TokenAmountInput } from './ui/masked-input';
28+
import { FeeAmountInput } from './ui/masked-input';
2929
import {
3030
Select,
3131
SelectContent,
@@ -61,10 +61,6 @@ export function AssignNftDialog({
6161

6262
const form = useForm<z.infer<typeof schema>>({
6363
resolver: zodResolver(schema),
64-
defaultValues: {
65-
profile: '',
66-
fee: '0',
67-
},
6864
});
6965

7066
const handler = (values: z.infer<typeof schema>) => {
@@ -133,10 +129,7 @@ export function AssignNftDialog({
133129
<Trans>Network Fee</Trans>
134130
</FormLabel>
135131
<FormControl>
136-
<TokenAmountInput
137-
{...field}
138-
aria-label={t`Network fee amount`}
139-
/>
132+
<FeeAmountInput {...field} />
140133
</FormControl>
141134
<FormMessage />
142135
</FormItem>

src/components/CoinsCard.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
commands,
3939
TransactionResponse,
4040
} from '../bindings';
41+
import { FeeAmountInput } from './ui/masked-input';
4142

4243
interface CoinsCardProps {
4344
precision: number;
@@ -204,9 +205,6 @@ export function CoinsCard({
204205

205206
const combineForm = useForm<z.infer<typeof combineFormSchema>>({
206207
resolver: zodResolver(combineFormSchema),
207-
defaultValues: {
208-
combineFee: '0',
209-
},
210208
});
211209

212210
const onCombineSubmit = (values: z.infer<typeof combineFormSchema>) => {
@@ -255,7 +253,6 @@ export function CoinsCard({
255253
resolver: zodResolver(splitFormSchema),
256254
defaultValues: {
257255
outputCount: 2,
258-
splitFee: '0',
259256
},
260257
});
261258

@@ -307,7 +304,6 @@ export function CoinsCard({
307304
const autoCombineForm = useForm<z.infer<typeof autoCombineFormSchema>>({
308305
resolver: zodResolver(autoCombineFormSchema),
309306
defaultValues: {
310-
autoCombineFee: '0',
311307
maxCoins: '100',
312308
maxCoinAmount: '',
313309
},
@@ -459,7 +455,7 @@ export function CoinsCard({
459455
<Trans>Network Fee</Trans>
460456
</FormLabel>
461457
<FormControl>
462-
<Input {...field} />
458+
<FeeAmountInput {...field} />
463459
</FormControl>
464460
<FormMessage />
465461
</FormItem>
@@ -527,7 +523,7 @@ export function CoinsCard({
527523
<Trans>Network Fee</Trans>
528524
</FormLabel>
529525
<FormControl>
530-
<Input {...field} />
526+
<FeeAmountInput {...field} />
531527
</FormControl>
532528
<FormMessage />
533529
</FormItem>
@@ -577,7 +573,7 @@ export function CoinsCard({
577573
<Trans>Network Fee</Trans>
578574
</FormLabel>
579575
<FormControl>
580-
<Input {...field} />
576+
<FeeAmountInput {...field} />
581577
</FormControl>
582578
<FormMessage />
583579
</FormItem>

src/components/FeeOnlyDialog.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
FormLabel,
2525
FormMessage,
2626
} from './ui/form';
27-
import { TokenAmountInput } from './ui/masked-input';
27+
import { FeeAmountInput } from './ui/masked-input';
2828

2929
export interface FeeOnlyDialogProps {
3030
title: string;
@@ -53,9 +53,6 @@ export function FeeOnlyDialog({
5353

5454
const form = useForm<z.infer<typeof schema>>({
5555
resolver: zodResolver(schema),
56-
defaultValues: {
57-
fee: '0',
58-
},
5956
});
6057

6158
const handler = (values: z.infer<typeof schema>) => {
@@ -80,11 +77,7 @@ export function FeeOnlyDialog({
8077
<Trans>Network Fee</Trans>
8178
</FormLabel>
8279
<FormControl>
83-
<TokenAmountInput
84-
{...field}
85-
placeholder={t`Enter network fee`}
86-
aria-label={t`Network fee amount`}
87-
/>
80+
<FeeAmountInput {...field} />
8881
</FormControl>
8982
<FormMessage />
9083
</FormItem>

src/components/NftCard.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ import {
6565
FormMessage,
6666
} from './ui/form';
6767
import { Input } from './ui/input';
68-
import { TokenAmountInput } from './ui/masked-input';
68+
import { FeeAmountInput } from './ui/masked-input';
6969
import {
7070
Select,
7171
SelectContent,
@@ -200,7 +200,6 @@ export function NftCard({ nft, updateNfts, selectionState }: NftCardProps) {
200200
defaultValues: {
201201
url: '',
202202
kind: 'data',
203-
fee: '0',
204203
},
205204
});
206205

@@ -607,7 +606,7 @@ export function NftCard({ nft, updateNfts, selectionState }: NftCardProps) {
607606
<Trans>Network Fee</Trans>
608607
</FormLabel>
609608
<FormControl>
610-
<TokenAmountInput {...field} />
609+
<FeeAmountInput {...field} />
611610
</FormControl>
612611
<FormMessage />
613612
</FormItem>

src/components/TransferDialog.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
FormMessage,
2424
} from './ui/form';
2525
import { Input } from './ui/input';
26-
import { TokenAmountInput } from './ui/masked-input';
26+
import { FeeAmountInput } from './ui/masked-input';
2727
import { Trans } from '@lingui/react/macro';
2828
import { t } from '@lingui/core/macro';
2929

@@ -55,7 +55,6 @@ export function TransferDialog({
5555
resolver: zodResolver(schema),
5656
defaultValues: {
5757
address: '',
58-
fee: '0',
5958
},
6059
});
6160

@@ -96,10 +95,7 @@ export function TransferDialog({
9695
<Trans>Network Fee</Trans>
9796
</FormLabel>
9897
<FormControl>
99-
<TokenAmountInput
100-
{...field}
101-
placeholder={t`Enter fee amount`}
102-
/>
98+
<FeeAmountInput {...field} />
10399
</FormControl>
104100
<FormMessage />
105101
</FormItem>

src/components/ui/masked-input.tsx

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import * as React from 'react';
33
import { NumericFormat, NumericFormatProps } from 'react-number-format';
44
import { toast } from 'react-toastify';
55
import { Input, InputProps } from './input';
6+
import { useDefaultFee } from '@/hooks/useDefaultFee';
7+
import { useWalletState } from '@/state';
68

79
interface MaskedInputProps extends NumericFormatProps<InputProps> {
810
inputRef?: React.Ref<HTMLInputElement>;
@@ -109,4 +111,58 @@ const IntegerInput = React.forwardRef<HTMLInputElement, IntegerInputProps>(
109111

110112
IntegerInput.displayName = 'IntegerInput';
111113

112-
export { MaskedInput, TokenAmountInput, IntegerInput };
114+
// Fee input that uses the default fee value as initial value
115+
interface FeeAmountInputProps extends Omit<XchInputProps, 'value'> {
116+
value?: string;
117+
className?: string;
118+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
119+
onValueChange?: (values: {
120+
floatValue: number | undefined;
121+
value: string;
122+
}) => void;
123+
}
124+
125+
const FeeAmountInput = React.forwardRef<HTMLInputElement, FeeAmountInputProps>(
126+
({ value, className, onChange, onValueChange, ...props }, ref) => {
127+
const { fee: defaultFee } = useDefaultFee();
128+
const walletState = useWalletState();
129+
const hasSetInitialValue = React.useRef(false);
130+
131+
// Set initial value when component mounts
132+
React.useEffect(() => {
133+
if (!value && !hasSetInitialValue.current) {
134+
hasSetInitialValue.current = true;
135+
if (onChange) {
136+
onChange({ target: { value: defaultFee } } as any);
137+
}
138+
if (onValueChange) {
139+
onValueChange({ floatValue: Number(defaultFee), value: defaultFee });
140+
}
141+
}
142+
}, [defaultFee, onChange, onValueChange, value]);
143+
144+
return (
145+
<div className='relative'>
146+
<TokenAmountInput
147+
{...props}
148+
ref={ref}
149+
value={value ?? defaultFee}
150+
onChange={onChange}
151+
onValueChange={onValueChange}
152+
placeholder={t`Enter network fee`}
153+
aria-label={t`Network fee amount`}
154+
className={`pr-12 ${className || ''}`}
155+
/>
156+
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3'>
157+
<span className='text-gray-500 text-sm' id='price-currency'>
158+
{walletState.sync.unit.ticker}
159+
</span>
160+
</div>
161+
</div>
162+
);
163+
},
164+
);
165+
166+
FeeAmountInput.displayName = 'FeeAmountInput';
167+
168+
export { MaskedInput, TokenAmountInput, IntegerInput, FeeAmountInput };

src/hooks/useDefaultFee.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useLocalStorage } from 'usehooks-ts';
2+
import * as React from 'react';
3+
4+
export interface DefaultFee {
5+
fee: string;
6+
}
7+
8+
const isValidFee = (value: string): boolean => {
9+
const num = parseFloat(value);
10+
return !isNaN(num) && num >= 0;
11+
};
12+
13+
const DEFAULT_FEE = '0';
14+
15+
export function useDefaultFee() {
16+
const [defaultFee, setDefaultFee] = useLocalStorage<DefaultFee>(
17+
'defaultFee',
18+
{ fee: DEFAULT_FEE },
19+
);
20+
21+
const setFee = (fee: string) => {
22+
if (isValidFee(fee)) {
23+
setDefaultFee({ fee });
24+
}
25+
};
26+
27+
// Ensure we always have a valid fee value
28+
React.useEffect(() => {
29+
if (!isValidFee(defaultFee.fee)) {
30+
setDefaultFee({ fee: DEFAULT_FEE });
31+
}
32+
}, [defaultFee.fee, setDefaultFee]);
33+
34+
return {
35+
fee: isValidFee(defaultFee.fee) ? defaultFee.fee : DEFAULT_FEE,
36+
setFee,
37+
};
38+
}

src/pages/CreateProfile.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import * as z from 'zod';
2121
import { commands, TransactionResponse } from '../bindings';
2222
import Container from '../components/Container';
2323
import { useWalletState } from '../state';
24-
import { TokenAmountInput } from '@/components/ui/masked-input';
24+
import { FeeAmountInput } from '@/components/ui/masked-input';
2525
import { Trans } from '@lingui/react/macro';
2626
import { t } from '@lingui/core/macro';
2727
import { CreateProfileConfirmation } from '@/components/confirmations/CreateProfileConfirmation';
@@ -88,12 +88,7 @@ export default function CreateProfile() {
8888
</FormLabel>
8989
<FormControl>
9090
<div className='relative'>
91-
<TokenAmountInput {...field} className='pr-12' />
92-
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3'>
93-
<span className='text-gray-500 text-sm'>
94-
{walletState.sync.unit.ticker}
95-
</span>
96-
</div>
91+
<FeeAmountInput {...field} className='pr-12' />
9792
</div>
9893
</FormControl>
9994
<FormMessage />

src/pages/IssueToken.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import * as z from 'zod';
2121
import { commands, TransactionResponse } from '../bindings';
2222
import Container from '../components/Container';
2323
import { useWalletState } from '../state';
24-
import { TokenAmountInput } from '@/components/ui/masked-input';
24+
import { TokenAmountInput, FeeAmountInput } from '@/components/ui/masked-input';
2525
import { Trans } from '@lingui/react/macro';
2626
import { t } from '@lingui/core/macro';
2727
import { TokenConfirmation } from '@/components/confirmations/TokenConfirmation';
@@ -127,16 +127,11 @@ export default function IssueToken() {
127127
render={({ field }) => (
128128
<FormItem>
129129
<FormLabel>
130-
<Trans>Fee</Trans>
130+
<Trans>Network Fee</Trans>
131131
</FormLabel>
132132
<FormControl>
133133
<div className='relative'>
134-
<TokenAmountInput {...field} className='pr-12' />
135-
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3'>
136-
<span className='text-gray-500 text-sm'>
137-
{walletState.sync.unit.ticker}
138-
</span>
139-
</div>
134+
<FeeAmountInput {...field} className='pr-12' />
140135
</div>
141136
</FormControl>
142137
<FormMessage />

src/pages/MakeOffer.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ import {
1616
} from '@/components/ui/dialog';
1717
import { Input } from '@/components/ui/input';
1818
import { Label } from '@/components/ui/label';
19-
import { IntegerInput, TokenAmountInput } from '@/components/ui/masked-input';
19+
import {
20+
IntegerInput,
21+
TokenAmountInput,
22+
FeeAmountInput,
23+
} from '@/components/ui/masked-input';
2024
import { Switch } from '@/components/ui/switch';
2125
import {
2226
Tooltip,
@@ -202,13 +206,10 @@ export function MakeOffer() {
202206
<Trans>Network Fee</Trans>
203207
</Label>
204208
<div className='relative'>
205-
<TokenAmountInput
209+
<FeeAmountInput
206210
id='fee'
207-
type='text'
208-
placeholder={'0.00'}
209211
className='pr-12'
210-
value={state.fee}
211-
onValueChange={(values) => {
212+
onValueChange={(values: { value: string }) => {
212213
setState({
213214
fee: values.value,
214215
});

src/pages/MintNft.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import * as z from 'zod';
3434
import { commands, TransactionResponse } from '../bindings';
3535
import Container from '../components/Container';
3636
import { useWalletState } from '../state';
37+
import { FeeAmountInput } from '@/components/ui/masked-input';
3738

3839
export default function MintNft() {
3940
const navigate = useNavigate();
@@ -272,17 +273,7 @@ export default function MintNft() {
272273
</FormLabel>
273274
<FormControl>
274275
<div className='relative'>
275-
<Input
276-
type='text'
277-
placeholder={'0.00'}
278-
{...field}
279-
className='pr-12'
280-
/>
281-
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3'>
282-
<span className='text-gray-500 text-sm'>
283-
{walletState.sync.unit.ticker}
284-
</span>
285-
</div>
276+
<FeeAmountInput {...field} className='pr-12' />
286277
</div>
287278
</FormControl>
288279
<FormMessage />

0 commit comments

Comments
 (0)