Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 12 additions & 83 deletions apps/web-wallet/app/features/send/send-scanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,96 +5,25 @@ import {
PageHeaderTitle,
} from '~/components/page';
import { QRScanner } from '~/components/qr-scanner';
import { useExchangeRate } from '~/hooks/use-exchange-rate';
import { useBuildLinkWithSearchParams } from '~/hooks/use-search-params-link';
import { useToast } from '~/hooks/use-toast';
import type { Money } from '~/lib/money';
import { useNavigateWithViewTransition } from '~/lib/transitions/view-transition';
import type { Account } from '../accounts/account';
import { DomainError, getErrorMessage } from '../shared/error';
import { useSendStore } from './send-provider';

/**
* Converts an amount to the send account currency.
* If the amount is already in the send account currency, returns the amount.
*
* @throws if the exchange rate fails to load
*/
const useConverter = (sendAccount: Account) => {
const otherCurrency = sendAccount.currency === 'BTC' ? 'USD' : 'BTC';

const { data: rate } = useExchangeRate(
`${otherCurrency}-${sendAccount.currency}`,
);

return (amount: Money) => {
if (!rate) throw new Error('Exchange rate not found');

return amount.convert(sendAccount.currency, rate);
};
};

export default function SendScanner() {
const { toast } = useToast();
const navigate = useNavigateWithViewTransition();
const buildLinkWithSearchParams = useBuildLinkWithSearchParams();

const sendAccount = useSendStore((state) => state.getSourceAccount());
const selectDestination = useSendStore((state) => state.selectDestination);
const continueSend = useSendStore((state) => state.proceedWithSend);

const convert = useConverter(sendAccount);

const handleDecode = async (input: string) => {
const selectDestinationResult = await selectDestination(input);
if (!selectDestinationResult.success) {
toast({
title: 'Invalid input',
description: selectDestinationResult.error,
variant: 'destructive',
});
return;
}

const { amount } = selectDestinationResult.data;

if (!amount) {
// Navigate to send input to enter the amount
return navigate(buildLinkWithSearchParams('/send'), {
applyTo: 'oldView',
transition: 'slideDown',
});
}

const convertedAmount =
amount.currency !== sendAccount.currency ? convert(amount) : undefined;
const result = await continueSend(amount, convertedAmount);

if (!result.success) {
const toastOptions =
result.error instanceof DomainError
? { description: result.error.message }
: {
title: 'Error',
description: getErrorMessage(
result.error,
'Failed to get a send quote. Please try again',
),
variant: 'destructive' as const,
};

toast(toastOptions);
return;
}

if (result.next !== 'confirmQuote') {
return;
}

navigate(buildLinkWithSearchParams('/send/confirm'), {
applyTo: 'newView',
transition: 'slideUp',
});
const handleDecode = (scannedContent: string) => {
if (!scannedContent) return;

const hash = `#${scannedContent}`;
// The hash needs to be set manually before navigating or the destination
// route's clientLoader won't see it.
// See https://github.com/remix-run/remix/discussions/10721
window.history.replaceState(null, '', hash);
navigate(
{ ...buildLinkWithSearchParams('/send'), hash },
{ transition: 'slideDown', applyTo: 'oldView' },
);
};

return (
Expand Down
Loading