Skip to content

Commit e0789bf

Browse files
authored
Merge pull request #207 from xch-dev/misc-fixes
Misc fixes
2 parents 04aedc9 + d234d86 commit e0789bf

File tree

4 files changed

+137
-90
lines changed

4 files changed

+137
-90
lines changed

src/components/Loading.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { cn } from '@/lib/utils';
2+
import { Loader2 } from 'lucide-react';
3+
4+
interface LoadingProps extends React.HTMLAttributes<HTMLDivElement> {
5+
size?: number;
6+
text?: string;
7+
}
8+
9+
export function Loading({
10+
size = 24,
11+
text,
12+
className,
13+
...props
14+
}: LoadingProps) {
15+
return (
16+
<div
17+
className={cn(
18+
'flex flex-col items-center justify-center gap-2',
19+
className,
20+
)}
21+
{...props}
22+
>
23+
<Loader2 className='animate-spin' style={{ width: size, height: size }} />
24+
{text && <p className='text-sm text-muted-foreground'>{text}</p>}
25+
</div>
26+
);
27+
}

src/pages/Send.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import ConfirmationDialog from '@/components/ConfirmationDialog';
2+
import Container from '@/components/Container';
23
import Header from '@/components/Header';
4+
import { TokenAmountInput } from '@/components/ui/masked-input';
35
import { Button } from '@/components/ui/button';
6+
import { Card, CardContent } from '@/components/ui/card';
47
import {
58
Form,
69
FormControl,
@@ -12,9 +15,10 @@ import {
1215
import { Input } from '@/components/ui/input';
1316
import { useErrors } from '@/hooks/useErrors';
1417
import { amount, positiveAmount } from '@/lib/formTypes';
15-
import { toMojos } from '@/lib/utils';
18+
import { toDecimal, toMojos } from '@/lib/utils';
1619
import { useWalletState } from '@/state';
1720
import { zodResolver } from '@hookform/resolvers/zod';
21+
import BigNumber from 'bignumber.js';
1822
import { useCallback, useEffect, useState } from 'react';
1923
import { useForm } from 'react-hook-form';
2024
import { useNavigate, useParams } from 'react-router-dom';
@@ -26,16 +30,12 @@ import {
2630
SendXch,
2731
TransactionResponse,
2832
} from '../bindings';
29-
import Container from '../components/Container';
30-
import { TokenAmountInput } from '@/components/ui/masked-input';
3133

3234
export default function Send() {
3335
const { asset_id: assetId } = useParams();
3436
const isXch = assetId === 'xch';
35-
3637
const navigate = useNavigate();
3738
const walletState = useWalletState();
38-
3939
const { addError } = useErrors();
4040

4141
const [asset, setAsset] = useState<(CatRecord & { decimals: number }) | null>(
@@ -69,7 +69,6 @@ export default function Send() {
6969

7070
const unlisten = events.syncEvent.listen((event) => {
7171
const type = event.payload.type;
72-
7372
if (
7473
type === 'coin_state' ||
7574
type === 'puzzle_batch_synced' ||
@@ -98,7 +97,13 @@ export default function Send() {
9897
(address) => commands.validateAddress(address).catch(addError),
9998
'Invalid address',
10099
),
101-
amount: positiveAmount(asset?.decimals || 12),
100+
amount: positiveAmount(asset?.decimals || 12).refine(
101+
(amount) =>
102+
asset
103+
? BigNumber(amount).lte(toDecimal(asset.balance, asset.decimals))
104+
: true,
105+
'Amount exceeds balance',
106+
),
102107
fee: amount(walletState.sync.unit.decimals).optional(),
103108
});
104109

@@ -135,6 +140,19 @@ export default function Send() {
135140
/>
136141

137142
<Container className='max-w-xl'>
143+
{asset && (
144+
<Card className='mb-6'>
145+
<CardContent className='pt-6'>
146+
<div className='text-sm text-muted-foreground'>
147+
Available Balance
148+
</div>
149+
<div className='text-2xl font-medium mt-1'>
150+
{toDecimal(asset.balance, asset.decimals)} {asset.ticker}
151+
</div>
152+
</CardContent>
153+
</Card>
154+
)}
155+
138156
<Form {...form}>
139157
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4'>
140158
<FormField

src/pages/TokenList.tsx

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useErrors } from '@/hooks/useErrors';
1616
import { usePrices } from '@/hooks/usePrices';
1717
import { useTokenParams } from '@/hooks/useTokenParams';
1818
import { toDecimal } from '@/lib/utils';
19-
import { ArrowDown10, ArrowDownAz, Coins, InfoIcon } from 'lucide-react';
19+
import { ArrowDown10, ArrowDownAz, Coins, InfoIcon, Clock } from 'lucide-react';
2020
import { useCallback, useEffect, useMemo, useState } from 'react';
2121
import { Link, useNavigate } from 'react-router-dom';
2222
import { CatRecord, commands, events } from '../bindings';
@@ -30,50 +30,48 @@ enum TokenView {
3030
export function TokenList() {
3131
const navigate = useNavigate();
3232
const walletState = useWalletState();
33-
3433
const { getBalanceInUsd } = usePrices();
3534
const { addError } = useErrors();
36-
3735
const [params, setParams] = useTokenParams();
3836
const { view, showHidden } = params;
39-
4037
const [cats, setCats] = useState<CatRecord[]>([]);
4138

4239
const catsWithBalanceInUsd = useMemo(
4340
() =>
44-
cats.map((cat) => ({
45-
...cat,
46-
balanceInUsd: getBalanceInUsd(cat.asset_id, toDecimal(cat.balance, 3)),
47-
})),
41+
cats.map((cat) => {
42+
const balance = Number(toDecimal(cat.balance, 3));
43+
const usdValue = parseFloat(
44+
getBalanceInUsd(cat.asset_id, balance.toString()),
45+
);
46+
return {
47+
...cat,
48+
balanceInUsd: usdValue,
49+
sortValue: usdValue,
50+
};
51+
}),
4852
[cats, getBalanceInUsd],
4953
);
5054

5155
const sortedCats = catsWithBalanceInUsd.sort((a, b) => {
52-
if (a.visible && !b.visible) {
53-
return -1;
54-
}
55-
56-
if (!a.visible && b.visible) {
57-
return 1;
58-
}
59-
60-
if (!a[view] && b[view]) {
61-
return -1;
62-
}
56+
if (a.visible && !b.visible) return -1;
57+
if (!a.visible && b.visible) return 1;
6358

64-
if (a[view] && !b[view]) {
65-
return 1;
59+
if (view === TokenView.Balance) {
60+
if (a.balanceInUsd === 0 && b.balanceInUsd === 0) {
61+
return (
62+
Number(toDecimal(b.balance, 3)) - Number(toDecimal(a.balance, 3))
63+
);
64+
}
65+
return b.sortValue - a.sortValue;
6666
}
6767

68-
if (!a[view] && !b[view]) {
69-
return 0;
70-
}
68+
const aName = a.name || 'Unknown CAT';
69+
const bName = b.name || 'Unknown CAT';
7170

72-
if (view === TokenView.Balance) {
73-
return Number(b.balanceInUsd) - Number(a.balanceInUsd);
74-
}
71+
if (aName === 'Unknown CAT' && bName !== 'Unknown CAT') return 1;
72+
if (bName === 'Unknown CAT' && aName !== 'Unknown CAT') return -1;
7573

76-
return a.name!.localeCompare(b.name!);
74+
return aName.localeCompare(bName);
7775
});
7876

7977
const visibleCats = sortedCats.filter((cat) => showHidden || cat.visible);
@@ -151,7 +149,6 @@ export function TokenList() {
151149
<Card className='transition-colors hover:bg-neutral-50 dark:hover:bg-neutral-900'>
152150
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
153151
<CardTitle className='text-md font-medium'>Chia</CardTitle>
154-
155152
<img
156153
alt={`XCH logo`}
157154
className='h-6 w-6'
@@ -187,7 +184,6 @@ export function TokenList() {
187184
<CardTitle className='text-md font-medium truncate'>
188185
{cat.name || 'Unknown CAT'}
189186
</CardTitle>
190-
191187
{cat.icon_url && (
192188
<img
193189
alt={`${cat.asset_id} logo`}
@@ -200,7 +196,6 @@ export function TokenList() {
200196
<div className='text-2xl font-medium truncate'>
201197
{toDecimal(cat.balance, 3)} {cat.ticker ?? ''}
202198
</div>
203-
204199
<div className='text-sm text-neutral-500'>
205200
~${cat.balanceInUsd}
206201
</div>

src/pages/ViewOffer.tsx

Lines changed: 59 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,47 @@ import { commands, OfferSummary, TakeOfferResponse } from '@/bindings';
22
import ConfirmationDialog from '@/components/ConfirmationDialog';
33
import Container from '@/components/Container';
44
import Header from '@/components/Header';
5+
import { Loading } from '@/components/Loading';
56
import { OfferCard } from '@/components/OfferCard';
67
import { Button } from '@/components/ui/button';
78
import { Input } from '@/components/ui/input';
89
import { Label } from '@/components/ui/label';
910
import { useErrors } from '@/hooks/useErrors';
10-
import { toDecimal, toMojos } from '@/lib/utils';
11+
import { toMojos } from '@/lib/utils';
1112
import { useWalletState } from '@/state';
12-
import BigNumber from 'bignumber.js';
1313
import { useEffect, useState } from 'react';
1414
import { useNavigate, useParams } from 'react-router-dom';
1515

1616
export function ViewOffer() {
1717
const { offer } = useParams();
1818
const { addError } = useErrors();
19-
2019
const walletState = useWalletState();
2120
const navigate = useNavigate();
2221

22+
const [isLoading, setIsLoading] = useState(true);
23+
const [loadingStatus, setLoadingStatus] = useState('Initializing...');
2324
const [summary, setSummary] = useState<OfferSummary | null>(null);
2425
const [response, setResponse] = useState<TakeOfferResponse | null>(null);
2526
const [fee, setFee] = useState('');
2627

2728
useEffect(() => {
2829
if (!offer) return;
2930

30-
commands
31-
.viewOffer({ offer })
32-
.then((data) => setSummary(data.offer))
33-
.catch(addError);
31+
const loadOffer = async () => {
32+
setIsLoading(true);
33+
setLoadingStatus('Fetching offer details...');
34+
35+
commands
36+
.viewOffer({ offer })
37+
.then((data) => {
38+
setSummary(data.offer);
39+
setLoadingStatus('Processing offer data...');
40+
})
41+
.catch(addError)
42+
.finally(() => setIsLoading(false));
43+
};
44+
45+
loadOffer();
3446
}, [offer, addError]);
3547

3648
const importOffer = () => {
@@ -40,18 +52,13 @@ export function ViewOffer() {
4052
.catch(addError);
4153
};
4254

43-
const take = () => {
44-
commands
45-
.importOffer({ offer: offer! })
46-
.then(() =>
47-
commands
48-
.takeOffer({
49-
offer: offer!,
50-
fee: toMojos(fee || '0', walletState.sync.unit.decimals),
51-
})
52-
.then((result) => setResponse(result))
53-
.catch(addError),
54-
)
55+
const take = async () => {
56+
await commands
57+
.takeOffer({
58+
offer: offer!,
59+
fee: toMojos(fee || '0', walletState.sync.unit.decimals),
60+
})
61+
.then((result) => setResponse(result))
5562
.catch(addError);
5663
};
5764

@@ -60,41 +67,41 @@ export function ViewOffer() {
6067
<Header title='View Offer' />
6168

6269
<Container>
63-
{summary && (
64-
<OfferCard summary={summary}>
65-
<div className='flex flex-col space-y-1.5'>
66-
<Label htmlFor='fee'>Network Fee</Label>
67-
<Input
68-
id='fee'
69-
type='text'
70-
placeholder='0.00'
71-
className='pr-12'
72-
value={fee}
73-
onChange={(e) => setFee(e.target.value)}
74-
onKeyDown={(event) => {
75-
if (event.key === 'Enter') {
76-
event.preventDefault();
77-
take();
78-
}
79-
}}
80-
/>
70+
{isLoading ? (
71+
<Loading className='my-8' text={loadingStatus} />
72+
) : (
73+
summary && (
74+
<>
75+
<OfferCard summary={summary}>
76+
<div className='flex flex-col space-y-1.5'>
77+
<Label htmlFor='fee'>Network Fee</Label>
78+
<Input
79+
id='fee'
80+
type='text'
81+
placeholder='0.00'
82+
className='pr-12'
83+
value={fee}
84+
onChange={(e) => setFee(e.target.value)}
85+
onKeyDown={(event) => {
86+
if (event.key === 'Enter') {
87+
event.preventDefault();
88+
take();
89+
}
90+
}}
91+
/>
92+
</div>
93+
</OfferCard>
8194

82-
<span className='text-xs text-muted-foreground'>
83-
{BigNumber(summary?.fee ?? '0').isGreaterThan(0)
84-
? `This does not include a fee of ${toDecimal(summary!.fee, walletState.sync.unit.decimals)} which was already added by the maker.`
85-
: ''}
86-
</span>
87-
</div>
88-
</OfferCard>
89-
)}
95+
<div className='mt-4 flex gap-2'>
96+
<Button variant='outline' onClick={importOffer}>
97+
Save Offer
98+
</Button>
9099

91-
<div className='mt-4 flex gap-2'>
92-
<Button variant='outline' onClick={importOffer}>
93-
Save Offer
94-
</Button>
95-
96-
<Button onClick={take}>Take Offer</Button>
97-
</div>
100+
<Button onClick={take}>Take Offer</Button>
101+
</div>
102+
</>
103+
)
104+
)}
98105
</Container>
99106

100107
<ConfirmationDialog

0 commit comments

Comments
 (0)